From 0546bc95eccb4815b21245915464b56c2260f9ba Mon Sep 17 00:00:00 2001 From: esanchezm Date: Tue, 4 Jul 2006 14:07:08 +0000 Subject: [PATCH] 2006-07-04 Esteban Sanchez * windows/pandora_wmi.*, windows/pandora_windows_info.*, windows/wmi/disphelper.*, misc/pandora_file.*, ssh/*, ssh/libssh2/*, modules/pandora_module_service.*, modules/pandora_module_proc.*: Added to repository * pandora_windows_service.cc, pandora_windows_service.h: getXMLHeader renamed to getXmlHeader and modified its behavior. Updated to the Pandora XML specification. Add SSH and XML generation functionallity. * modules/pandora_module_exec.cc: Added the execution to a job to prevent zombies process. Update the execution command. * modules/pandora_module_factory.cc: Added Pandora_Module_Proc and Pandora_Module_Service. Added the interval parameter. * modules/pandora_module_list.cc: Added support to Pandora_Module_Proc and Pandora_Module_Service. * modules/pandora_module.h: Added new types of modules. Added execution times and module description to the Pandora_Module class. Added support to Pandora_Module_Proc and Pandora_Module_Service. Added getXml, setInterval and setDescription functions. * pandora.h: Commentary fixed. * Changelog: Renamed to ChangeLog. git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@83 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f --- pandora_agents/win32/ChangeLog | 29 + pandora_agents/win32/Changelog | 3 - pandora_agents/win32/PandoraAgent.dev | 342 +++- pandora_agents/win32/main.cc | 5 +- pandora_agents/win32/misc/pandora_file.cc | 63 + pandora_agents/win32/misc/pandora_file.h | 41 + .../win32/modules/pandora_module.cc | 78 +- pandora_agents/win32/modules/pandora_module.h | 12 +- .../win32/modules/pandora_module_exec.cc | 56 +- .../win32/modules/pandora_module_exec.h | 5 + .../win32/modules/pandora_module_factory.cc | 52 +- .../win32/modules/pandora_module_list.cc | 21 +- .../win32/modules/pandora_module_proc.cc | 49 + .../win32/modules/pandora_module_proc.h | 37 + .../win32/modules/pandora_module_service.cc | 47 + .../win32/modules/pandora_module_service.h | 38 + pandora_agents/win32/pandora.cc | 1 + pandora_agents/win32/pandora.h | 2 +- pandora_agents/win32/pandora_agent_conf.cc | 1 + pandora_agents/win32/pandora_strutils.h | 1 - .../win32/pandora_windows_service.cc | 198 ++- .../win32/pandora_windows_service.h | 25 +- pandora_agents/win32/ssh/libssh2/channel.c | 1247 ++++++++++++++ pandora_agents/win32/ssh/libssh2/comp.c | 286 ++++ pandora_agents/win32/ssh/libssh2/crypt.c | 189 +++ pandora_agents/win32/ssh/libssh2/hostkey.c | 546 ++++++ pandora_agents/win32/ssh/libssh2/kex.c | 1382 ++++++++++++++++ pandora_agents/win32/ssh/libssh2/libssh2.h | 400 +++++ .../win32/ssh/libssh2/libssh2_config.h | 38 + .../win32/ssh/libssh2/libssh2_priv.h | 479 ++++++ .../win32/ssh/libssh2/libssh2_publickey.h | 101 ++ .../win32/ssh/libssh2/libssh2_sftp.h | 198 +++ pandora_agents/win32/ssh/libssh2/mac.c | 271 +++ pandora_agents/win32/ssh/libssh2/misc.c | 210 +++ pandora_agents/win32/ssh/libssh2/packet.c | 1182 +++++++++++++ pandora_agents/win32/ssh/libssh2/publickey.c | 728 ++++++++ pandora_agents/win32/ssh/libssh2/scp.c | 437 +++++ pandora_agents/win32/ssh/libssh2/session.c | 966 +++++++++++ pandora_agents/win32/ssh/libssh2/sftp.c | 1473 +++++++++++++++++ pandora_agents/win32/ssh/libssh2/userauth.c | 855 ++++++++++ .../win32/ssh/pandora_ssh_client.cc | 249 +++ pandora_agents/win32/ssh/pandora_ssh_client.h | 112 ++ pandora_agents/win32/ssh/pandora_ssh_test.cc | 157 ++ pandora_agents/win32/ssh/pandora_ssh_test.h | 34 + .../win32/windows/pandora_windows_info.cc | 58 + .../win32/windows/pandora_windows_info.h | 41 + pandora_agents/win32/windows/pandora_wmi.cc | 267 +++ pandora_agents/win32/windows/pandora_wmi.h | 41 + pandora_agents/win32/windows/wmi/disphelper.c | 1292 +++++++++++++++ pandora_agents/win32/windows/wmi/disphelper.h | 731 ++++++++ 50 files changed, 14919 insertions(+), 157 deletions(-) create mode 100644 pandora_agents/win32/ChangeLog delete mode 100644 pandora_agents/win32/Changelog create mode 100644 pandora_agents/win32/misc/pandora_file.cc create mode 100644 pandora_agents/win32/misc/pandora_file.h create mode 100644 pandora_agents/win32/modules/pandora_module_proc.cc create mode 100644 pandora_agents/win32/modules/pandora_module_proc.h create mode 100644 pandora_agents/win32/modules/pandora_module_service.cc create mode 100644 pandora_agents/win32/modules/pandora_module_service.h create mode 100644 pandora_agents/win32/ssh/libssh2/channel.c create mode 100644 pandora_agents/win32/ssh/libssh2/comp.c create mode 100644 pandora_agents/win32/ssh/libssh2/crypt.c create mode 100644 pandora_agents/win32/ssh/libssh2/hostkey.c create mode 100644 pandora_agents/win32/ssh/libssh2/kex.c create mode 100644 pandora_agents/win32/ssh/libssh2/libssh2.h create mode 100644 pandora_agents/win32/ssh/libssh2/libssh2_config.h create mode 100644 pandora_agents/win32/ssh/libssh2/libssh2_priv.h create mode 100644 pandora_agents/win32/ssh/libssh2/libssh2_publickey.h create mode 100644 pandora_agents/win32/ssh/libssh2/libssh2_sftp.h create mode 100644 pandora_agents/win32/ssh/libssh2/mac.c create mode 100644 pandora_agents/win32/ssh/libssh2/misc.c create mode 100644 pandora_agents/win32/ssh/libssh2/packet.c create mode 100644 pandora_agents/win32/ssh/libssh2/publickey.c create mode 100644 pandora_agents/win32/ssh/libssh2/scp.c create mode 100644 pandora_agents/win32/ssh/libssh2/session.c create mode 100644 pandora_agents/win32/ssh/libssh2/sftp.c create mode 100644 pandora_agents/win32/ssh/libssh2/userauth.c create mode 100644 pandora_agents/win32/ssh/pandora_ssh_client.cc create mode 100644 pandora_agents/win32/ssh/pandora_ssh_client.h create mode 100644 pandora_agents/win32/ssh/pandora_ssh_test.cc create mode 100644 pandora_agents/win32/ssh/pandora_ssh_test.h create mode 100644 pandora_agents/win32/windows/pandora_windows_info.cc create mode 100644 pandora_agents/win32/windows/pandora_windows_info.h create mode 100644 pandora_agents/win32/windows/pandora_wmi.cc create mode 100644 pandora_agents/win32/windows/pandora_wmi.h create mode 100644 pandora_agents/win32/windows/wmi/disphelper.c create mode 100644 pandora_agents/win32/windows/wmi/disphelper.h diff --git a/pandora_agents/win32/ChangeLog b/pandora_agents/win32/ChangeLog new file mode 100644 index 0000000000..718b824baa --- /dev/null +++ b/pandora_agents/win32/ChangeLog @@ -0,0 +1,29 @@ +2006-07-04 Esteban Sanchez + + * windows/pandora_wmi.*, windows/pandora_windows_info.*, windows/wmi/disphelper.*, + misc/pandora_file.*, ssh/*, ssh/libssh2/*, modules/pandora_module_service.*, + modules/pandora_module_proc.*: Added to repository + + * pandora_windows_service.cc, pandora_windows_service.h: getXMLHeader renamed to getXmlHeader + and modified its behavior. Updated to the Pandora XML specification. Add SSH and XML generation + functionallity. + + * modules/pandora_module_exec.cc: Added the execution to a job to prevent zombies process. Update + the execution command. + + * modules/pandora_module_factory.cc: Added Pandora_Module_Proc and Pandora_Module_Service. Added + the interval parameter. + + * modules/pandora_module_list.cc: Added support to Pandora_Module_Proc and Pandora_Module_Service. + + * modules/pandora_module.h: Added new types of modules. Added execution times and module description + to the Pandora_Module class. Added support to Pandora_Module_Proc and Pandora_Module_Service. Added + getXml, setInterval and setDescription functions. + + * pandora.h: Commentary fixed. + + * Changelog: Renamed to ChangeLog + +2006-06-20 Esteban Sanchez + + * Initial import. diff --git a/pandora_agents/win32/Changelog b/pandora_agents/win32/Changelog deleted file mode 100644 index 122b293723..0000000000 --- a/pandora_agents/win32/Changelog +++ /dev/null @@ -1,3 +0,0 @@ -2006-06-20 Esteban Sánchez - -* Initial import. diff --git a/pandora_agents/win32/PandoraAgent.dev b/pandora_agents/win32/PandoraAgent.dev index 298fe10726..96639d2658 100644 --- a/pandora_agents/win32/PandoraAgent.dev +++ b/pandora_agents/win32/PandoraAgent.dev @@ -1,7 +1,7 @@ [Project] FileName=PandoraAgent.dev Name=PandoraAgent -UnitCount=25 +UnitCount=59 Type=1 Ver=1 ObjFiles= @@ -12,22 +12,22 @@ ResourceIncludes= MakeIncludes= Compiler= CppCompiler= -Linker= +Linker=-lole32 -loleaut32 -luuid_@@_e:/work/common/Dev-Cpp/lib/libws2_32.a_@@_e:/work/common/Dev-Cpp/lib/libpsapi.a_@@_e:/work/common/Dev-Cpp/lib/libws2_32.a_@@_e:/work/common/Dev-Cpp/lib/libeay32.a_@@_e:/work/common/Dev-Cpp/lib/libz.lib_@@_e:/work/common/Dev-Cpp/lib/libiphlpapi.a_@@_e:/work/common/Dev-Cpp/lib/libnetapi32.a_@@_ IsCpp=1 Icon= ExeOutput= ObjectOutput= OverrideOutput=0 -OverrideOutputName= +OverrideOutputName=PandoraAgent.exe HostApplication= -Folders=Modules,Modules/Utils,XML +Folders=Misc,Modules,Modules/Utils,SSH,SSH/libssh2,Windows,Windows/WMI,XML CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 -CompilerSettings= +CompilerSettings=0000000000000001000000 [Unit1] FileName=main.cc @@ -198,7 +198,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=pandora_strutils.cpp +FileName=pandora_strutils.cc CompileCpp=1 Folder= Compile=1 @@ -257,30 +257,20 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit26] -FileName=modules\pandora_module_factory.h -CompileCpp=1 -Folder=Modules/Utils -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - [Unit28] -FileName=modules\pandora_module.cc -CompileCpp=1 -Folder=Modules +FileName=windows\wmi\disphelper.c +CompileCpp=0 +Folder=Windows/WMI Compile=1 Link=1 Priority=1000 OverrideBuildCmd=0 -BuildCmd= +BuildCmd=$(CC) -c disphelper.c -o disphelper.o $(CFLAGS) [Unit29] -FileName=modules\pandora_module_generic_data_inc.cc +FileName=windows\wmi\disphelper.h CompileCpp=1 -Folder=Modules +Folder=Windows/WMI Compile=1 Link=1 Priority=1000 @@ -288,7 +278,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=modules\pandora_module_generic_proc.cc +FileName=modules\pandora_module_proc.h CompileCpp=1 Folder=Modules Compile=1 @@ -298,7 +288,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=modules\pandora_module_generic_data_string.cc +FileName=modules\pandora_module_proc.cc CompileCpp=1 Folder=Modules Compile=1 @@ -308,19 +298,9 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=modules\pandora_module_exec.cc +FileName=ssh\pandora_ssh_client.cc CompileCpp=1 -Folder=Modules -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit32] -FileName=modules\pandora_module_exec.h -CompileCpp=1 -Folder=Modules +Folder=SSH Compile=1 Link=1 Priority=1000 @@ -367,3 +347,293 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= +[Unit26] +FileName=windows\pandora_wmi.cc +CompileCpp=1 +Folder=Windows +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit27] +FileName=windows\pandora_wmi.h +CompileCpp=1 +Folder=Windows +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit32] +FileName=ssh\pandora_ssh_client.h +CompileCpp=1 +Folder=SSH +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit34] +FileName=ssh\libssh2\userauth.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit35] +FileName=ssh\libssh2\comp.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit36] +FileName=ssh\libssh2\crypt.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit37] +FileName=ssh\libssh2\kex.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit38] +FileName=ssh\libssh2\libssh2.h +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit39] +FileName=ssh\libssh2\libssh2_config.h +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit40] +FileName=ssh\libssh2\libssh2_publickey.h +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit41] +FileName=ssh\libssh2\mac.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit42] +FileName=ssh\libssh2\packet.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] +FileName=ssh\libssh2\publickey.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit44] +FileName=ssh\libssh2\scp.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit45] +FileName=ssh\libssh2\session.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit47] +FileName=ssh\libssh2\libssh2_priv.h +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit48] +FileName=ssh\libssh2\libssh2_sftp.h +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit49] +FileName=ssh\libssh2\misc.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit50] +FileName=ssh\libssh2\sftp.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit51] +FileName=ssh\libssh2\channel.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] +FileName=ssh\pandora_ssh_test.h +CompileCpp=1 +Folder=SSH +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit53] +FileName=ssh\pandora_ssh_test.cc +CompileCpp=1 +Folder=SSH +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit46] +FileName=ssh\libssh2\hostkey.c +CompileCpp=1 +Folder=SSH/libssh2 +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit54] +FileName=misc\pandora_file.cc +CompileCpp=1 +Folder=Misc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] +FileName=misc\pandora_file.h +CompileCpp=1 +Folder=Misc +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit56] +FileName=windows\pandora_windows_info.h +CompileCpp=1 +Folder=Windows +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit57] +FileName=windows\pandora_windows_info.cc +CompileCpp=1 +Folder=Windows +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit59] +FileName=modules\pandora_module_service.cc +CompileCpp=1 +Folder=Modules +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit58] +FileName=modules\pandora_module_service.h +CompileCpp=1 +Folder=Modules +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/pandora_agents/win32/main.cc b/pandora_agents/win32/main.cc index 0e57cf240c..8aef01fa20 100644 --- a/pandora_agents/win32/main.cc +++ b/pandora_agents/win32/main.cc @@ -59,9 +59,8 @@ main (int argc, char *argv[]) { return 1; } } - //service->run (); - service->pandora_init (); - service->pandora_run (); + service->run (); + delete service; return 0; } diff --git a/pandora_agents/win32/misc/pandora_file.cc b/pandora_agents/win32/misc/pandora_file.cc new file mode 100644 index 0000000000..0dc96b54f4 --- /dev/null +++ b/pandora_agents/win32/misc/pandora_file.cc @@ -0,0 +1,63 @@ +/* Misc utils for files. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_file.h" +#include +#include +#include + +using namespace std; + +string +Pandora_File::readFile (const string filename) { + string line, result; + ifstream myfile (filename.c_str ()); + + if (! myfile.is_open ()) { + throw File_Not_Found (); + } + + if (myfile.is_open()) { + while (! myfile.eof()) { + getline (myfile,line); + result += line + '\n'; + } + myfile.close(); + } + return result; +} + +void +Pandora_File::removeFile (const string filename) { + if (remove (filename.c_str ()) == -1) { + throw Delete_Error (); + } +} + +void +Pandora_File::writeFile (const string filename, const string data) { + ofstream file (filename.c_str ()); + + if (! file.is_open ()) { + throw File_Not_Found (); + } + file.write (data.c_str (), data.length ()); + file.close (); +} diff --git a/pandora_agents/win32/misc/pandora_file.h b/pandora_agents/win32/misc/pandora_file.h new file mode 100644 index 0000000000..ad0f6d911d --- /dev/null +++ b/pandora_agents/win32/misc/pandora_file.h @@ -0,0 +1,41 @@ +/* Misc utils for files. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_FILE_H__ +#define __PANDORA_FILE_H__ + +#include +#include "../pandora.h" + +using namespace std; + +namespace Pandora_File { + class File_Not_Found : Pandora::Pandora_Exception { + }; + + class Delete_Error : Pandora::Pandora_Exception { + }; + + string readFile (const string filename); + void removeFile (const string filename); + void writeFile (const string filename, const string data); +} + +#endif diff --git a/pandora_agents/win32/modules/pandora_module.cc b/pandora_agents/win32/modules/pandora_module.cc index 36685d20cf..b9841f5811 100644 --- a/pandora_agents/win32/modules/pandora_module.cc +++ b/pandora_agents/win32/modules/pandora_module.cc @@ -20,11 +20,20 @@ #include "pandora_module.h" #include "../pandora_strutils.h" +#include "../pandora.h" +using namespace Pandora; using namespace Pandora_Modules; +using namespace Pandora_Strutils; Pandora_Module::Pandora_Module (string name) { - this->module_name = name; + this->module_name = name; + this->executions = 0; + this->module_interval = 1; + this->output = ""; + this->max = 0; + this->min = 0; + this->has_limits = false; } Pandora_Module::~Pandora_Module () { @@ -97,6 +106,62 @@ Pandora_Module::run () { } +TiXmlElement * +Pandora_Module::getXml () { + string data; + TiXmlElement *root; + TiXmlElement *element; + TiXmlText *text; + string item_str, data_str, desc_str; + + pandoraDebug ("%s getXML begin", module_name.c_str ()); + + try { + data = this->getOutput (); + } catch (Output_Error e) { + pandoraLog ("Output error"); + return NULL; + } catch (Interval_Error e) { + pandoraLog ("The returned value was not in the interval"); + return NULL; + } + + root = new TiXmlElement ("module"); + + element = new TiXmlElement ("name"); + text = new TiXmlText (this->module_name); + element->InsertEndChild (*text); + root->InsertEndChild (*element); + delete element; + delete text; + + element = new TiXmlElement ("type"); + text = new TiXmlText (this->module_type_str); + element->InsertEndChild (*text); + root->InsertEndChild (*element); + delete element; + delete text; + + element = new TiXmlElement ("data"); + data_str = strreplace (this->output, + "%", "%%" ); + text = new TiXmlText (data_str); + element->InsertEndChild (*text); + root->InsertEndChild (*element); + delete text; + delete element; + + element = new TiXmlElement ("description"); + text = new TiXmlText (this->module_description); + element->InsertEndChild (*text); + root->InsertEndChild (*element); + delete text; + delete element; + + pandoraDebug ("%s getXML end", module_name.c_str ()); + return root; +} + void Pandora_Module::setMax (int value) { this->has_limits = true; @@ -114,3 +179,14 @@ Pandora_Module::setType (string type) { this->module_type_str = type; this->module_type = getModuleType (type); } + +void +Pandora_Module::setInterval (int interval) { + this->module_interval = interval; +} + +void +Pandora_Module::setDescription (string description) { + pandoraDebug ("Set description to: %s", description.c_str ()); + this->module_description = description; +} diff --git a/pandora_agents/win32/modules/pandora_module.h b/pandora_agents/win32/modules/pandora_module.h index 8319c62f8b..02758b1828 100644 --- a/pandora_agents/win32/modules/pandora_module.h +++ b/pandora_agents/win32/modules/pandora_module.h @@ -43,9 +43,14 @@ namespace Pandora_Modules { enum { MODULE_0, - MODULE_EXEC + MODULE_EXEC, + MODULE_PROC, + MODULE_SERVICE }; + const string module_exec_str = "module_exec"; + const string module_proc_str = "module_proc"; + const string module_service_str = "module_service"; class Output_Error : public Pandora::Pandora_Exception { }; class Interval_Error : public Pandora::Pandora_Exception { }; @@ -56,8 +61,10 @@ namespace Pandora_Modules { string module_type_str; int module_type; string module_kind_str; + string module_description; int module_kind; int module_interval; + int executions; string output; int max, min; bool has_limits; @@ -70,7 +77,7 @@ namespace Pandora_Modules { void setInterval (int interval); /* Get the XML output of the agent. */ - TiXmlElement *getXML (); + TiXmlElement *getXml (); /* Execute the agent */ virtual void run (); @@ -83,6 +90,7 @@ namespace Pandora_Modules { int getModuleKind () const; void setType (string type); + void setDescription (string description); void setMax (int value); void setMin (int value); }; diff --git a/pandora_agents/win32/modules/pandora_module_exec.cc b/pandora_agents/win32/modules/pandora_module_exec.cc index c176b0b434..e41d3d58c4 100644 --- a/pandora_agents/win32/modules/pandora_module_exec.cc +++ b/pandora_agents/win32/modules/pandora_module_exec.cc @@ -19,7 +19,6 @@ */ #include "pandora_module_exec.h" -#include "pandora_module.h" #include "../pandora_strutils.h" #include @@ -31,7 +30,7 @@ using namespace Pandora_Modules; Pandora_Module_Exec::Pandora_Module_Exec (string name, string exec) : Pandora_Module (name) { - this->module_exec = "cmd.exe /c " + exec; + this->module_exec = "cmd.exe /c \"" + exec + "\""; this->module_kind_str = module_exec_str; this->module_kind = MODULE_EXEC; @@ -43,21 +42,41 @@ Pandora_Module_Exec::run () { PROCESS_INFORMATION pi; DWORD retval; SECURITY_ATTRIBUTES attributes; - HANDLE out, new_stdout, out_read; + HANDLE out, new_stdout, out_read, job; string working_dir; this->output = ""; + /* Check the interval */ + if (this->executions % this->module_interval != 0) { + pandoraDebug ("Interval is not fulfilled"); + this->executions++; + return; + } + + /* Increment the executions after check. This is done to execute the + first time */ + this->executions++; + /* Set the bInheritHandle flag so pipe handles are inherited. */ attributes.nLength = sizeof (SECURITY_ATTRIBUTES); attributes.bInheritHandle = TRUE; attributes.lpSecurityDescriptor = NULL; + /* Create a job to kill the child tree if it become zombie */ + /* CAUTION: In order to work this, WINVER should be defined to 0x0500. + It is defined in */ + job = CreateJobObject (&attributes, this->module_name.c_str ()); + if (job == NULL) { + pandoraLog ("CreateJobObject bad. Err: %d", GetLastError ()); + return; + } + /* Get the handle to the current STDOUT. */ out = GetStdHandle (STD_OUTPUT_HANDLE); if (! CreatePipe (&out_read, &new_stdout, &attributes, 0)) { - pandoraLog ("CreatePipe failed"); + pandoraLog ("CreatePipe failed. Err: %d", GetLastError ()); return; } @@ -83,22 +102,32 @@ Pandora_Module_Exec::run () { working_dir = getPandoraInstallDir () + "util\\"; /* Create the child process. */ - if (! CreateProcess (NULL, (CHAR *) this->module_exec.c_str (), NULL, NULL, TRUE, - CREATE_NEW_CONSOLE, NULL, working_dir.c_str (), - &si, &pi)) { - pandoraLog ("Babel_Windows_Password: %s CreateProcess failed (%d)", + if (! CreateProcess (NULL, (CHAR *) this->module_exec.c_str (), NULL, + NULL, TRUE, CREATE_SUSPENDED, NULL, + working_dir.c_str (), &si, &pi)) { + pandoraLog ("Pandora_Module_Exec: %s CreateProcess failed. Err: %d", this->module_name.c_str (), GetLastError ()); } else { char buffer[BUFSIZE + 1]; unsigned long read, avail; + if (! AssignProcessToJobObject (job, pi.hProcess)) { + pandoraLog ("Assign bad %d", GetLastError ()); + } + ResumeThread (pi.hThread); + /* Wait until process exits. */ - /* TODO: This should not be infinite, but just a few seconds */ - WaitForSingleObject (pi.hProcess, INFINITE); + /* TODO: The time should be an attribute*/ + WaitForSingleObject (pi.hProcess, 15000); GetExitCodeProcess (pi.hProcess, &retval); if (retval != 0) { - pandoraLog ("Pandora_Module: %s did not executed well (retcode: %d)", + if (! TerminateJobObject (job, 0)) { + pandoraLog ("TerminateJobObject failed. Err: %d", + GetLastError ()); + } + + pandoraLog ("Pandora_Module_Exec: %s did not executed well (retcode: %d)", this->module_name.c_str (), retval); } @@ -109,12 +138,13 @@ Pandora_Module_Exec::run () { do { ReadFile (out_read, buffer, BUFSIZE, &read, NULL); + buffer[read] = '\0'; this->output += (char *) buffer; - memset (buffer, 0, BUFSIZE); } while (read >= BUFSIZE); } - /* Close process and thread handles. */ + /* Close job, process and thread handles. */ + CloseHandle (job); CloseHandle (pi.hProcess); CloseHandle (pi.hThread); } diff --git a/pandora_agents/win32/modules/pandora_module_exec.h b/pandora_agents/win32/modules/pandora_module_exec.h index 320bc75a50..5b1c4c23b8 100644 --- a/pandora_agents/win32/modules/pandora_module_exec.h +++ b/pandora_agents/win32/modules/pandora_module_exec.h @@ -18,6 +18,9 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef __PANDORA_MODULE_EXEC_H__ +#define __PANDORA_MODULE_EXEC_H__ + #include "pandora_module.h" namespace Pandora_Modules { @@ -30,3 +33,5 @@ namespace Pandora_Modules { void run (); }; } + +#endif diff --git a/pandora_agents/win32/modules/pandora_module_factory.cc b/pandora_agents/win32/modules/pandora_module_factory.cc index e6e4e141ea..95d6bb785c 100644 --- a/pandora_agents/win32/modules/pandora_module_factory.cc +++ b/pandora_agents/win32/modules/pandora_module_factory.cc @@ -21,6 +21,8 @@ #include "pandora_module_factory.h" #include "pandora_module.h" #include "pandora_module_exec.h" +#include "pandora_module_proc.h" +#include "pandora_module_service.h" #include "../pandora_strutils.h" #include @@ -32,9 +34,11 @@ using namespace Pandora_Strutils; #define TOKEN_TYPE ("module_type ") #define TOKEN_INTERVAL ("module_interval ") #define TOKEN_EXEC ("module_exec ") +#define TOKEN_PROC ("module_proc ") +#define TOKEN_SERVICE ("module_service ") #define TOKEN_MAX ("module_max ") #define TOKEN_MIN ("module_min ") -#define TOKEN_DESCRIPTION ("module_descripcion ") +#define TOKEN_DESCRIPTION ("module_description ") string parseLine (string line, string token) { @@ -54,10 +58,21 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { list::iterator iter; string module_name, module_type, module_exec; string module_min, module_max, module_description; - string module_interval; + string module_interval, module_proc, module_service; Pandora_Module *module; bool numeric; - + + module_name = ""; + module_type = ""; + module_min = ""; + module_max = ""; + module_description = ""; + module_interval = ""; + module_exec = ""; + module_proc = ""; + module_service = ""; + + stringtok (tokens, definition, "\n"); /* Pick the first and the last value of the token list */ @@ -79,6 +94,12 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { if (module_exec == "") { module_exec = parseLine (line, TOKEN_EXEC); } + if (module_proc == "") { + module_proc = parseLine (line, TOKEN_PROC); + } + if (module_service == "") { + module_service = parseLine (line, TOKEN_SERVICE); + } if (module_max == "") { module_max = parseLine (line, TOKEN_MAX); } @@ -95,10 +116,20 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { if (module_exec != "") { module = new Pandora_Module_Exec (module_name, module_exec); + } else if (module_proc != "") { + module = new Pandora_Module_Proc (module_name, + module_proc); + } else if (module_service != "") { + module = new Pandora_Module_Service (module_name, + module_service); } else { return NULL; } - + pandoraDebug ("Description %s", module_description.c_str ()); + if (module_description != "") { + module->setDescription (module_description); + } + switch (Pandora_Module::getModuleType (module_type)) { case TYPE_GENERIC_DATA: case TYPE_GENERIC_DATA_INC: @@ -145,5 +176,18 @@ Pandora_Module_Factory::getModuleFromDefinition (string definition) { } } } + + if (module_interval != "") { + int interval; + + try { + interval = strtoint (module_interval); + module->setInterval (interval); + } catch (Invalid_Conversion e) { + pandoraLog ("Invalid interval value \"%s\" for module %s", + module_interval.c_str (), + module_name.c_str ()); + } + } return module; } diff --git a/pandora_agents/win32/modules/pandora_module_list.cc b/pandora_agents/win32/modules/pandora_module_list.cc index 58d36d1214..0aa4381e12 100644 --- a/pandora_agents/win32/modules/pandora_module_list.cc +++ b/pandora_agents/win32/modules/pandora_module_list.cc @@ -21,14 +21,14 @@ #include "pandora_module_factory.h" #include "pandora_module_list.h" #include "pandora_module_exec.h" +#include "pandora_module_proc.h" +#include "pandora_module_service.h" #include using namespace std; using namespace Pandora; using namespace Pandora_Module_Factory; - - Pandora_Module_List::Pandora_Module_List (string filename) { ifstream file (filename.c_str ()); string buffer; @@ -85,8 +85,10 @@ Pandora_Module_List::~Pandora_Module_List () { void Pandora_Module_List::parseModuleDefinition (string definition) { - Pandora_Module *module; - Pandora_Module_Exec *module_exec; + Pandora_Module *module; + Pandora_Module_Exec *module_exec; + Pandora_Module_Proc *module_proc; + Pandora_Module_Service *module_service; module = getModuleFromDefinition (definition); @@ -96,6 +98,17 @@ Pandora_Module_List::parseModuleDefinition (string definition) { module_exec = (Pandora_Module_Exec *) module; modules->push_back (module_exec); + break; + case MODULE_PROC: + module_proc = (Pandora_Module_Proc *) module; + modules->push_back (module_proc); + + break; + + case MODULE_SERVICE: + module_service = (Pandora_Module_Service *) module; + modules->push_back (module_service); + break; default: break; diff --git a/pandora_agents/win32/modules/pandora_module_proc.cc b/pandora_agents/win32/modules/pandora_module_proc.cc new file mode 100644 index 0000000000..9359d65faa --- /dev/null +++ b/pandora_agents/win32/modules/pandora_module_proc.cc @@ -0,0 +1,49 @@ +/* Pandora proc module. These modules check if a program is alive in the system. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_module_proc.h" +#include "../windows/pandora_wmi.h" +#include "../pandora_strutils.h" +#include +#include + +using namespace Pandora; +using namespace Pandora_Modules; +using namespace Pandora_Strutils; + +Pandora_Module_Proc::Pandora_Module_Proc (string name, string process_name) + : Pandora_Module (name) { + + transform (process_name.begin (), process_name.end (), + this->process_name.begin (), (int (*) (int)) tolower); + + this->module_kind_str = module_proc_str; + this->module_kind = MODULE_PROC; +} + +void +Pandora_Module_Proc::run () { + pandoraDebug ("MODULE_PROC RUN"); + + + int res = Pandora_Wmi::isProcessRunning (this->process_name); + pandoraDebug ("res: %d", res); + output = "1"; +} diff --git a/pandora_agents/win32/modules/pandora_module_proc.h b/pandora_agents/win32/modules/pandora_module_proc.h new file mode 100644 index 0000000000..0e833ed134 --- /dev/null +++ b/pandora_agents/win32/modules/pandora_module_proc.h @@ -0,0 +1,37 @@ +/* Pandora proc module. These modules check if a program is alive in the system. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_MODULE_PROC_H__ +#define __PANDORA_MODULE_PROC_H__ + +#include "pandora_module.h" + +namespace Pandora_Modules { + class Pandora_Module_Proc : public Pandora_Module { + private: + string process_name; + public: + Pandora_Module_Proc (string name, string process_name); + + void run (); + }; +} + +#endif diff --git a/pandora_agents/win32/modules/pandora_module_service.cc b/pandora_agents/win32/modules/pandora_module_service.cc new file mode 100644 index 0000000000..509f142ad9 --- /dev/null +++ b/pandora_agents/win32/modules/pandora_module_service.cc @@ -0,0 +1,47 @@ +/* Pandora service module. These modules check if a service is running in the + system. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_module_service.h" +#include "../windows/pandora_wmi.h" +#include "../pandora_strutils.h" +#include +#include + +using namespace Pandora; +using namespace Pandora_Modules; +using namespace Pandora_Strutils; + +Pandora_Module_Service::Pandora_Module_Service (string name, string service_name) + : Pandora_Module (name) { + + transform (service_name.begin (), service_name.end (), + this->service_name.begin (), (int (*) (int)) tolower); + + this->module_kind_str = module_service_str; + this->module_kind = MODULE_SERVICE; +} + +void +Pandora_Module_Service::run () { + pandoraDebug ("MODULE_SERVICE RUN"); + + output = inttostr (Pandora_Wmi::isServiceRunning (this->service_name)); +} diff --git a/pandora_agents/win32/modules/pandora_module_service.h b/pandora_agents/win32/modules/pandora_module_service.h new file mode 100644 index 0000000000..d1a8a47fb3 --- /dev/null +++ b/pandora_agents/win32/modules/pandora_module_service.h @@ -0,0 +1,38 @@ +/* Pandora service module. These modules check if a service is running in the + system. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_MODULE_SERVICE_H__ +#define __PANDORA_MODULE_SERVICE_H__ + +#include "pandora_module.h" + +namespace Pandora_Modules { + class Pandora_Module_Service : public Pandora_Module { + private: + string service_name; + public: + Pandora_Module_Service (string name, string service_name); + + void run (); + }; +} + +#endif diff --git a/pandora_agents/win32/pandora.cc b/pandora_agents/win32/pandora.cc index 9d3993dc81..6b3315b46b 100644 --- a/pandora_agents/win32/pandora.cc +++ b/pandora_agents/win32/pandora.cc @@ -159,3 +159,4 @@ void Pandora::setPandoraDebug (bool dbg) { pandora_debug = dbg; } + diff --git a/pandora_agents/win32/pandora.h b/pandora_agents/win32/pandora.h index 5134507d72..b6f50dd5be 100644 --- a/pandora_agents/win32/pandora.h +++ b/pandora_agents/win32/pandora.h @@ -59,4 +59,4 @@ namespace Pandora { class Pandora_Exception { }; } -#endif /* __BABEL_H__ */ +#endif /* __PANDORA_H__ */ diff --git a/pandora_agents/win32/pandora_agent_conf.cc b/pandora_agents/win32/pandora_agent_conf.cc index 742010b64d..b21af44960 100644 --- a/pandora_agents/win32/pandora_agent_conf.cc +++ b/pandora_agents/win32/pandora_agent_conf.cc @@ -34,6 +34,7 @@ Pandora_Agent_Conf::Pandora_Agent_Conf (string filename) { this->key_values = new list (); if (!file.is_open ()) { + pandoraDebug ("No hay conf"); return; } diff --git a/pandora_agents/win32/pandora_strutils.h b/pandora_agents/win32/pandora_strutils.h index 392593df14..9e6892f52c 100644 --- a/pandora_agents/win32/pandora_strutils.h +++ b/pandora_agents/win32/pandora_strutils.h @@ -44,6 +44,5 @@ namespace Pandora_Strutils { void stringtok (list &l, string const &s, char const * const separators = " \t\n"); - } #endif /* __STRUTILS_H__ */ diff --git a/pandora_agents/win32/pandora_windows_service.cc b/pandora_agents/win32/pandora_windows_service.cc index 449378c396..a65996250d 100644 --- a/pandora_agents/win32/pandora_windows_service.cc +++ b/pandora_agents/win32/pandora_windows_service.cc @@ -20,13 +20,13 @@ #include "pandora_windows_service.h" #include "pandora.h" +#include "pandora_strutils.h" #include "windows_service.h" #include "modules/pandora_module_factory.h" +#include "ssh/pandora_ssh_client.h" +#include "misc/pandora_file.h" +#include "windows/pandora_windows_info.h" -#include "modules/pandora_module_generic_data.h" -#include "modules/pandora_module_generic_data_inc.h" -#include "modules/pandora_module_generic_data_string.h" -#include "modules/pandora_module_generic_proc.h" #include #include #include @@ -34,12 +34,12 @@ using namespace std; using namespace Pandora; using namespace Pandora_Modules; +using namespace Pandora_Strutils; Pandora_Windows_Service::Pandora_Windows_Service (const char * svc_name, const char * svc_display_name, const char * svc_description) - : Windows_Service (svc_name, svc_display_name, svc_description) - { + : Windows_Service (svc_name, svc_display_name, svc_description) { this->setInitFunction ((void (Windows_Service::*) ()) &Pandora_Windows_Service::pandora_init); this->setRunFunction ((void (Windows_Service::*) ()) &Pandora_Windows_Service::pandora_run); @@ -74,85 +74,55 @@ Pandora_Windows_Service::pandora_init () { this->setSleepTime (interval_ms); pandoraDebug ("Init end"); - return; } -void -Pandora_Windows_Service::addXMLHeader (TiXmlElement *root) { +TiXmlElement * +Pandora_Windows_Service::getXmlHeader () { TiXmlElement *agent; - TiXmlElement *element; - TiXmlText *text; SYSTEMTIME st; char timestamp[20]; + string value; - agent = new TiXmlElement ("agent"); - - element = new TiXmlElement ("name"); + agent = new TiXmlElement ("agent_data"); + /* TODO: Get the name of the machine if there is no agent_name*/ - text = new TiXmlText ("agent_name"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("version"); + value = conf->getValue ("agent_name"); + agent->SetAttribute ("agent_name", value); + /* TODO: Get the real version of the agent */ - text = new TiXmlText ("1.0Beta"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("timestamp"); + agent->SetAttribute ("version", "1.2Beta"); + GetSystemTime(&st); sprintf (timestamp, "%d/%d/%d %d:%d:%d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); - text = new TiXmlText (timestamp); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("interval"); - text = new TiXmlText ("interval"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("os"); - /* TODO */ - text = new TiXmlText ("Windows"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("os_version"); - /* TODO */ - text = new TiXmlText ("XP"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - element = new TiXmlElement ("os_build"); - /* TODO */ - text = new TiXmlText ("1"); - element->InsertEndChild (*text); - agent->InsertEndChild (*element); - delete text; - delete element; - - root->InsertEndChild (*agent); - delete agent; + agent->SetAttribute ("timestamp", timestamp); + + value = conf->getValue ("interval"); + agent->SetAttribute ("interval", value); + + value = Pandora_Windows_Info::getOSName (); + agent->SetAttribute ("os", value); + + value = Pandora_Windows_Info::getOSVersion (); + agent->SetAttribute ("os_version", value); + + return agent; } void Pandora_Windows_Service::pandora_run () { - + TiXmlDocument *doc; + TiXmlElement *local_xml, *agent; + string xml_filename, remote_host; + string remote_filepath, random_integer; + string tmp_filename, tmp_filepath, interval; + string pubkey_file, privkey_file; + bool saved; + pandoraDebug ("Run begin"); + agent = getXmlHeader (); + execution_number++; if (this->modules != NULL) { @@ -167,18 +137,92 @@ Pandora_Windows_Service::pandora_run () { pandoraDebug ("Run %s", module->getName ().c_str ()); module->run (); - try { - result = module->getOutput (); - - pandoraDebug ("Result: %s", result.c_str ()); - } catch (Output_Error e) { - pandoraLog ("Output error"); - } catch (Interval_Error e) { - pandoraLog ("The returned value was not in the interval"); - } + local_xml = module->getXml (); + agent->InsertEndChild (*local_xml); + + delete local_xml; this->modules->goNext (); } } + + random_integer = inttostr (rand()); + tmp_filename = conf->getValue ("agent_name"); + tmp_filename += "." + random_integer + ".data"; + + xml_filename = conf->getValue ("temporal"); + if (xml_filename[xml_filename.length () - 1] != '\\') { + xml_filename += "\\"; + } + tmp_filepath = xml_filename + tmp_filename; + + /* Copy the XML to a temporal file */ + pandoraDebug ("Copying XML on %s", tmp_filepath.c_str ()); + doc = new TiXmlDocument (tmp_filepath); + doc->InsertEndChild (*agent); + saved = doc->SaveFile(); + delete doc; + delete agent; + + if (!saved) { + pandoraLog ("Error when saving the XML in %s", + tmp_filepath.c_str ()); + return; + } + + remote_host = conf->getValue ("server_ip"); + ssh_client = new SSH::Pandora_Ssh_Client (); + pandoraDebug ("Connecting with %s", remote_host.c_str ()); + + try { + pubkey_file = Pandora::getPandoraInstallDir (); + pubkey_file += "key\\id_dsa.pub"; + privkey_file = Pandora::getPandoraInstallDir (); + privkey_file += "key\\id_dsa"; + pandoraDebug ("Pub: %s Priv: %s", pubkey_file.c_str (), + privkey_file.c_str ()); + ssh_client->connectWithPublicKey (remote_host.c_str (), 22, "babel", + pubkey_file, privkey_file, ""); + } catch (SSH::Authentication_Failed e) { + delete ssh_client; + pandoraLog ("Pandora Agent: Authentication Failed when connecting to %s", + remote_host.c_str ()); + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_File::Delete_Error e) { + } + return; + } catch (Pandora_Exception e) { + delete ssh_client; + pandoraLog ("Pandora Agent: Failed when copying to %s", remote_host.c_str ()); + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_File::Delete_Error e) { + } + return; + } + + remote_filepath = conf->getValue ("server_path"); + if (remote_filepath[remote_filepath.length () - 1] != '/') { + remote_filepath += "/"; + } + + pandoraDebug ("Remote copying XML %s on server %s at %s%s", + tmp_filepath.c_str (), remote_host.c_str (), + remote_filepath.c_str (), tmp_filename.c_str ()); + try { + ssh_client->scpFileFilename (remote_filepath + tmp_filename, + tmp_filepath); + } catch (Pandora_Exception e) { + ssh_client->disconnect(); + delete ssh_client; + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_File::Delete_Error e) { + } + return; + } + + ssh_client->disconnect(); pandoraDebug ("Execution number %d", execution_number); diff --git a/pandora_agents/win32/pandora_windows_service.h b/pandora_agents/win32/pandora_windows_service.h index f18a4f00c0..ac2776c1a5 100644 --- a/pandora_agents/win32/pandora_windows_service.h +++ b/pandora_agents/win32/pandora_windows_service.h @@ -26,25 +26,26 @@ #include "tinyxml/tinyxml.h" #include "pandora_agent_conf.h" #include "modules/pandora_module_list.h" +#include "ssh/pandora_ssh_client.h" using namespace std; class Pandora_Windows_Service : public Windows_Service { private: - //SSH::Babel_Ssh_Client *ssh_client; - Pandora_Agent_Conf *conf; - Pandora_Module_List *modules; - long execution_number; + SSH::Pandora_Ssh_Client *ssh_client; + Pandora_Agent_Conf *conf; + Pandora_Module_List *modules; + long execution_number; - void addXMLHeader (TiXmlElement *root); - public: - void pandora_run (); - void pandora_init (); + TiXmlElement * getXmlHeader (); + + void pandora_run (); + void pandora_init (); public: - Pandora_Windows_Service (const char * svc_name, - const char * svc_display_name, - const char * svc_description); - ~Pandora_Windows_Service (); + Pandora_Windows_Service (const char * svc_name, + const char * svc_display_name, + const char * svc_description); + ~Pandora_Windows_Service (); }; #endif diff --git a/pandora_agents/win32/ssh/libssh2/channel.c b/pandora_agents/win32/ssh/libssh2/channel.c new file mode 100644 index 0000000000..705ab0fd12 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/channel.c @@ -0,0 +1,1247 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#ifndef WIN32 +#include +#endif + +/* {{{ libssh2_channel_nextid + * Determine the next channel ID we can use at our end + */ +unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session) +{ + unsigned long id = session->next_channel; + LIBSSH2_CHANNEL *channel; + + channel = session->channels.head; + + while (channel) { + if (channel->local.id > id) { + id = channel->local.id; + } + channel = channel->next; + } + + /* This is a shortcut to avoid waiting for close packets on channels we've forgotten about, + * This *could* be a problem if we request and close 4 billion or so channels in too rapid succession + * for the remote end to respond, but the worst case scenario is that some data meant for another channel + * Gets picked up by the new one.... Pretty unlikely all told... + */ + session->next_channel = id + 1; +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocated new channel ID#%lu", id); +#endif + return id; +} +/* }}} */ + +/* {{{ libssh2_channel_locate + * Locate a channel pointer by number + */ +LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id) +{ + LIBSSH2_CHANNEL *channel = session->channels.head; + while (channel) { + if (channel->local.id == channel_id) { + return channel; + } + channel = channel->next; + } + + return NULL; +} +/* }}} */ + +#define libssh2_channel_add(session, channel) \ +{ \ + if ((session)->channels.tail) { \ + (session)->channels.tail->next = (channel); \ + (channel)->prev = (session)->channels.tail; \ + } else { \ + (session)->channels.head = (channel); \ + (channel)->prev = NULL; \ + } \ + (channel)->next = NULL; \ + (session)->channels.tail = (channel); \ + (channel)->session = (session); \ +} + +/* {{{ libssh2_channel_open_session + * Establish a generic session channel + */ +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, int channel_type_len, int window_size, int packet_size, + const char *message, int message_len) +{ + unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_OPEN_FAILURE, 0 }; + LIBSSH2_CHANNEL *channel = NULL; + unsigned long local_channel = libssh2_channel_nextid(session); + unsigned char *s, *packet = NULL; + unsigned long packet_len = channel_type_len + message_len + 17; /* packet_type(1) + channel_type_len(4) + sender_channel(4) + + window_size(4) + packet_size(4) */ + unsigned char *data = NULL; + unsigned long data_len; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Opening Channel - win %d pack %d", window_size, packet_size); +#endif + channel = (LIBSSH2_CHANNEL *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0); + return NULL; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->channel_type_len = channel_type_len; + channel->channel_type = (unsigned char *) LIBSSH2_ALLOC(session, channel_type_len); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating memory for channel type name", 0); + LIBSSH2_FREE(session, channel); + return NULL; + } + memcpy(channel->channel_type, channel_type, channel_type_len); + + /* REMEMBER: local as in locally sourced */ + channel->local.id = local_channel; + channel->remote.window_size = window_size; + channel->remote.window_size_initial = window_size; + channel->remote.packet_size = packet_size; + + libssh2_channel_add(session, channel); + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate temporary space for packet", 0); + return NULL; + } + *(s++) = SSH_MSG_CHANNEL_OPEN; + libssh2_htonu32(s, channel_type_len); s += 4; + memcpy(s, channel_type, channel_type_len); s += channel_type_len; + + libssh2_htonu32(s, local_channel); s += 4; + libssh2_htonu32(s, window_size); s += 4; + libssh2_htonu32(s, packet_size); s += 4; + + if (message && message_len) { + memcpy(s, message, message_len); s += message_len; + } + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-open request", 0); + goto channel_error; + } + + if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, packet + 5 + channel_type_len, 4)) { + goto channel_error; + } + + if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { + channel->remote.id = libssh2_ntohu32(data + 5); + channel->local.window_size = libssh2_ntohu32(data + 9); + channel->local.window_size_initial = libssh2_ntohu32(data + 9); + channel->local.packet_size = libssh2_ntohu32(data + 13); +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, channel->remote.window_size, + channel->local.packet_size, channel->remote.packet_size); +#endif + LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, data); + + return channel; + } + + if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0); + } + + channel_error: + + if (data) { + LIBSSH2_FREE(session, data); + } + if (packet) { + LIBSSH2_FREE(session, packet); + } + if (channel) { + unsigned char channel_id[4]; + LIBSSH2_FREE(session, channel->channel_type); + + if (channel->next) { + channel->next->prev = channel->prev; + } + if (channel->prev) { + channel->prev->next = channel->next; + } + if (session->channels.head == channel) { + session->channels.head = channel->next; + } + if (session->channels.tail == channel) { + session->channels.tail = channel->prev; + } + + /* Clear out packets meant for this channel */ + libssh2_htonu32(channel_id, channel->local.id); + while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) || + (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) { + LIBSSH2_FREE(session, data); + } + + LIBSSH2_FREE(session, channel); + } + + return NULL; +} +/* }}} */ + +/* {{{ libssh2_channel_direct_tcpip_ex + * Tunnel TCP/IP connect through the SSH session to direct host/port + */ +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, char *host, int port, char *shost, int sport) +{ + LIBSSH2_CHANNEL *channel; + unsigned char *message, *s; + unsigned long host_len = strlen(host), shost_len = strlen(shost); + unsigned long message_len = host_len + shost_len + 16; /* host_len(4) + port(4) + shost_len(4) + sport(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting direct-tcpip session to from %s:%d to %s:%d", shost, sport, host, port); +#endif + + s = message = (unsigned char *) LIBSSH2_ALLOC(session, message_len); + if (!message) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0); + return NULL; + } + libssh2_htonu32(s, host_len); s += 4; + memcpy(s, host, host_len); s += host_len; + libssh2_htonu32(s, port); s += 4; + + libssh2_htonu32(s, shost_len); s += 4; + memcpy(s, shost, shost_len); s += shost_len; + libssh2_htonu32(s, sport); s += 4; + + channel = (LIBSSH2_CHANNEL *) libssh2_channel_open_ex(session, "direct-tcpip", sizeof("direct-tcpip") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, (char *) message, message_len); + LIBSSH2_FREE(session, message); + + return channel; +} +/* }}} */ + +/* {{{ libssh2_channel_forward_listen_ex + * Bind a port on the remote host and listen for connections + */ +LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize) +{ + unsigned char *packet, *s, *data, reply_codes[3] = { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 }; + unsigned long data_len; + unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1)); + unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14; + /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting tcpip-forward session for %s:%d", host, port); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); + return NULL; + } + + *(s++) = SSH_MSG_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("tcpip-forward") - 1); s += 4; + memcpy(s, "tcpip-forward", sizeof("tcpip-forward") - 1); s += sizeof("tcpip-forward") - 1; + *(s++) = 0xFF; /* want_reply */ + + libssh2_htonu32(s, host_len); s += 4; + memcpy(s, host ? host : "0.0.0.0", host_len); s += host_len; + libssh2_htonu32(s, port); s += 4; + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0); + LIBSSH2_FREE(session, packet); + return NULL; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return NULL; + } + + if (data[0] == SSH_MSG_REQUEST_SUCCESS) { + LIBSSH2_LISTENER *listener; + + listener = (LIBSSH2_LISTENER *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER)); + if (!listener) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); + LIBSSH2_FREE(session, data); + return NULL; + } + memset(listener, 0, sizeof(LIBSSH2_LISTENER)); + listener->session = session; + listener->host = (char *) LIBSSH2_ALLOC(session, host_len + 1); + if (!listener->host) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0); + LIBSSH2_FREE(session, listener); + LIBSSH2_FREE(session, data); + return NULL; + } + memcpy(listener->host, host ? host : "0.0.0.0", host_len); + listener->host[host_len] = 0; + if (data_len >= 5 && !port) { + listener->port = libssh2_ntohu32(data + 1); +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Dynamic tcpip-forward port allocated: %d", listener->port); +#endif + } else { + listener->port = port; + } + + listener->queue_size = 0; + listener->queue_maxsize = queue_maxsize; + + listener->next = session->listeners; + listener->prev = NULL; + if (session->listeners) { + session->listeners->prev = listener; + } + session->listeners = listener; + + if (bound_port) { + *bound_port = listener->port; + } + + LIBSSH2_FREE(session, data); + return listener; + } + + if (data[0] == SSH_MSG_REQUEST_FAILURE) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0); + return NULL; + } + + return NULL; +} +/* }}} */ + +/* {{{ libssh2_channel_forward_cancel + * Stop listening on a remote port and free the listener + * Toss out any pending (un-accept()ed) connections + */ +LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener) +{ + LIBSSH2_SESSION *session = listener->session; + LIBSSH2_CHANNEL *queued = listener->queue; + unsigned char *packet, *s; + unsigned long host_len = strlen(listener->host); + unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1; + /* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Cancelling tcpip-forward session for %s:%d", listener->host, listener->port); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); + return -1; + } + + *(s++) = SSH_MSG_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("cancel-tcpip-forward") - 1); s += 4; + memcpy(s, "cancel-tcpip-forward", sizeof("cancel-tcpip-forward") - 1); s += sizeof("cancel-tcpip-forward") - 1; + *(s++) = 0x00; /* want_reply */ + + libssh2_htonu32(s, host_len); s += 4; + memcpy(s, listener->host, host_len); s += host_len; + libssh2_htonu32(s, listener->port); s += 4; + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send global-request packet for forward listen request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + while (queued) { + LIBSSH2_CHANNEL *next = queued->next; + + libssh2_channel_free(queued); + queued = next; + } + LIBSSH2_FREE(session, listener->host); + + if (listener->next) { + listener->next->prev = listener->prev; + } + if (listener->prev) { + listener->prev->next = listener->next; + } else { + session->listeners = listener->next; + } + + LIBSSH2_FREE(session, listener); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_channel_forward_accept + * Accept a connection + */ +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener) +{ + while (libssh2_packet_read(listener->session, 0) > 0); + + if (listener->queue) { + LIBSSH2_SESSION *session = listener->session; + LIBSSH2_CHANNEL *channel; + + channel = listener->queue; + + listener->queue = listener->queue->next; + if (listener->queue) { + listener->queue->prev = NULL; + } + + channel->prev = NULL; + channel->next = session->channels.head; + session->channels.head = channel; + + if (channel->next) { + channel->next->prev = channel; + } else { + session->channels.tail = channel; + } + listener->queue_size--; + + return channel; + } + + return NULL; +} +/* }}} */ + +/* {{{ libssh2_channel_setenv_ex + * Set an environment variable prior to requesting a shell/program/subsystem + */ +LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4]; + unsigned long data_len; + unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" + + want_reply(1) + varname_len(4) + value_len(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting remote environment variable: %s=%s on channel %lu/%lu", varname, value, channel->local.id, channel->remote.id); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("env") - 1); s += 4; + memcpy(s, "env", sizeof("env") - 1); s += sizeof("env") - 1; + + *(s++) = 0xFF; + + libssh2_htonu32(s, varname_len); s += 4; + memcpy(s, varname, varname_len); s += varname_len; + + libssh2_htonu32(s, value_len); s += 4; + memcpy(s, value, value_len); s += value_len; + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-request packet for setenv request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + libssh2_htonu32(local_channel, channel->local.id); + if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0); + return -1; +} +/* }}} */ + +/* {{{ libssh2_channel_request_pty_ex + * Duh... Request a PTY + */ +LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *term, int term_len, + char *modes, int modes_len, + int width, int height, + int width_px, int height_px) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4]; + unsigned long data_len; + unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) + + term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("pty-req") - 1); s += 4; + memcpy(s, "pty-req", sizeof("pty-req") - 1); s += sizeof("pty-req") - 1; + + *(s++) = 0xFF; + + libssh2_htonu32(s, term_len); s += 4; + if (term) { + memcpy(s, term, term_len); s += term_len; + } + + libssh2_htonu32(s, width); s += 4; + libssh2_htonu32(s, height); s += 4; + libssh2_htonu32(s, width_px); s += 4; + libssh2_htonu32(s, height_px); s += 4; + + libssh2_htonu32(s, modes_len); s += 4; + if (modes) { + memcpy(s, modes, modes_len); s += modes_len; + } + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send pty-request packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + libssh2_htonu32(local_channel, channel->local.id); + if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0); + return -1; +} +/* }}} */ + +/* Keep this an even number */ +#define LIBSSH2_X11_RANDOM_COOKIE_LEN 32 +/* {{{ libssh2_channel_x11_req_ex + * Request X11 forwarding + */ +LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4]; + unsigned long data_len; + unsigned long proto_len = auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1); + unsigned long cookie_len = auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN; + unsigned long packet_len = proto_len + cookie_len + 30; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) + + single_cnx(1) + proto_len(4) + cookie_len(4) + screen_num(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d", + channel->local.id, channel->remote.id, single_connection, + auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", + auth_cookie ? auth_cookie : "", screen_number); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, sizeof("x11-req") - 1); s += 4; + memcpy(s, "x11-req", sizeof("x11-req") - 1); s += sizeof("x11-req") - 1; + + *(s++) = 0xFF; /* want_reply */ + *(s++) = single_connection ? 0xFF : 0x00; + + libssh2_htonu32(s, proto_len); s += 4; + memcpy(s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len); + s += proto_len; + + libssh2_htonu32(s, cookie_len); s += 4; + if (auth_cookie) { + memcpy(s, auth_cookie, cookie_len); + } else { + int i; + char buffer[LIBSSH2_X11_RANDOM_COOKIE_LEN / 2]; + + RAND_bytes((unsigned char *) buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); + for (i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { + snprintf((char *) s + (i * 2), 2, "%02X", buffer[i]); + } + } + s += cookie_len; + + libssh2_htonu32(s, screen_number); s += 4; + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send x11-req packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + libssh2_htonu32(local_channel, channel->local.id); + + if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel x11-req", 0); + return -1; +} +/* }}} */ + +/* {{{ libssh2_channel_process_startup + * Primitive for libssh2_channel_(shell|exec|subsystem) + */ +LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, int request_len, const char *message, int message_len) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4]; + unsigned long data_len; + unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */ + + if (message) { + packet_len += message_len + 4; + } + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "starting request(%s) on channel %lu/%lu, message=%s", request, channel->local.id, channel->remote.id, message); +#endif + + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0); + return -1; + } + + *(s++) = SSH_MSG_CHANNEL_REQUEST; + libssh2_htonu32(s, channel->remote.id); s += 4; + libssh2_htonu32(s, request_len); s += 4; + memcpy(s, request, request_len); s += request_len; + + *(s++) = 0xFF; + + if (message) { + libssh2_htonu32(s, message_len); s += 4; + memcpy(s, message, message_len); s += message_len; + } + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + libssh2_htonu32(local_channel, channel->local.id); + if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) { + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0); + return -1; +} +/* }}} */ + +/* {{{ libssh2_channel_set_blocking + * Set a channel's blocking mode on or off, similar to a socket's fcntl(fd, F_SETFL, O_NONBLOCK); type command + */ +LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking) +{ +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting blocking mode on channel %lu/%lu to %d", channel->local.id, channel->remote.id, blocking); +#endif + channel->blocking = blocking; +} +/* }}} */ + +/* {{{ libssh2_channel_flush_ex + * Flush data from one (or all) stream + * Returns number of bytes flushed, or -1 on failure + */ +LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid) +{ + LIBSSH2_PACKET *packet = channel->session->packets.head; + unsigned long refund_bytes = 0, flush_bytes = 0; + + while (packet) { + LIBSSH2_PACKET *next = packet->next; + unsigned char packet_type = packet->data[0]; + + if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) && + (libssh2_ntohu32(packet->data + 1) == channel->local.id)) { + /* It's our channel at least */ + unsigned long packet_stream_id = (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 : libssh2_ntohu32(packet->data + 5); + if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) || + ((packet_type == (unsigned char) SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == (int) LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid == (int) packet_stream_id))) || + ((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) { + int bytes_to_flush = packet->data_len - packet->data_head; +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush, + packet_stream_id, channel->local.id, channel->remote.id); +#endif + + /* It's one of the streams we wanted to flush */ + refund_bytes += packet->data_len - 13; + flush_bytes += bytes_to_flush; + + LIBSSH2_FREE(channel->session, packet->data); + if (packet->prev) { + packet->prev->next = packet->next; + } else { + channel->session->packets.head = packet->next; + } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + channel->session->packets.tail = packet->prev; + } + LIBSSH2_FREE(channel->session, packet); + } + } + packet = next; + } + + if (refund_bytes) { + libssh2_channel_receive_window_adjust(channel, refund_bytes, 0); + } + + return flush_bytes; +} +/* }}} */ + +/* {{{ libssh2_channel_get_exit_status + * Return the channel's program exit status + */ +LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel) +{ + return channel->exit_status; +} + +/* }}} */ + +/* {{{ libssh2_channel_receive_window_adjust + * Adjust the receive window for a channel by adjustment bytes + * If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 + * The adjustment amount will be queued for a later packet + * + * Returns the new size of the receive window (as understood by remote end) + */ +LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force) +{ + unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */ + + if (!force && (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) { +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Queing %lu bytes for receive window adjustment for channel %lu/%lu", adjustment, channel->local.id, channel->remote.id); +#endif + channel->adjust_queue += adjustment; + return channel->remote.window_size; + } + + if (!adjustment && !channel->adjust_queue) { + return channel->remote.window_size; + } + + adjustment += channel->adjust_queue; + channel->adjust_queue = 0; + + + /* Adjust the window based on the block we just freed */ + adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; + libssh2_htonu32(adjust + 1, channel->remote.id); + libssh2_htonu32(adjust + 5, adjustment); +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Adjusting window %lu bytes for data flushed from channel %lu/%lu", adjustment, channel->local.id, channel->remote.id); +#endif + + if (libssh2_packet_write(channel->session, adjust, 9)) { + libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, deferring", 0); + channel->adjust_queue = adjustment; + } else { + channel->remote.window_size += adjustment; + } + + return channel->remote.window_size; +} +/* }}} */ + +/* {{{ libssh2_channel_handle_extended_data + * How should extended data look to the calling app? + * Keep it in separate channels[_read() _read_stdder()]? (NORMAL) + * Merge the extended data to the standard data? [everything via _read()]? (MERGE) + * Ignore it entirely [toss out packets as they come in]? (IGNORE) + */ +LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode) +{ +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting channel %lu/%lu handle_extended_data mode to %d", channel->local.id, channel->remote.id, ignore_mode); +#endif + channel->remote.extended_data_ignore_mode = ignore_mode; + + if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { + libssh2_channel_flush_ex(channel, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA); + } +} +/* }}} */ + +/* {{{ libssh2_channel_read_ex + * Read data from a channel + */ +LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen) +{ + LIBSSH2_SESSION *session = channel->session; + int bytes_read = 0, blocking_read = 0; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Attempting to read %d bytes from channel %lu/%lu stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id); +#endif + do { + LIBSSH2_PACKET *packet; + + /* Process any waiting packets */ + while (libssh2_packet_read(session, blocking_read) > 0) blocking_read = 0; + packet = session->packets.head; + + while (packet && (bytes_read < (int) buflen)) { + /* In case packet gets destroyed during this iteration */ + LIBSSH2_PACKET *next = packet->next; + + /* Either we asked for a specific extended data stream (and data was available), + * or the standard stream (and data was available), + * or the standard stream with extended_data_merge enabled and data was available + */ + if ((stream_id && (packet->data[0] == (char) SSH_MSG_CHANNEL_EXTENDED_DATA) && (channel->local.id == (unsigned int) libssh2_ntohu32(packet->data + 1)) && (stream_id == (int) libssh2_ntohu32(packet->data + 5))) || + (!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) || + (!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1)) && (channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { + int want = buflen - bytes_read; + int unlink_packet = 0; + + if (want >= (int) (packet->data_len - packet->data_head)) { + want = packet->data_len - packet->data_head; + unlink_packet = 1; + } + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Reading %d of buffered data from %lu/%lu/%d", want, channel->local.id, channel->remote.id, stream_id); +#endif + memcpy(buf + bytes_read, packet->data + packet->data_head, want); + packet->data_head += want; + bytes_read += want; + + if (unlink_packet) { + if (packet->prev) { + packet->prev->next = packet->next; + } else { + session->packets.head = packet->next; + } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + session->packets.tail = packet->prev; + } + LIBSSH2_FREE(session, packet->data); + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Unlinking empty packet buffer from channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + libssh2_channel_receive_window_adjust(channel, packet->data_len - (stream_id ? 13 : 9), 0); + LIBSSH2_FREE(session, packet); + } + } + packet = next; + } + blocking_read = 1; + } while (channel->blocking && (bytes_read == 0) && !channel->remote.close); + + if (channel->blocking && (bytes_read == 0)) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "Remote end has closed this channel", 0); + } + + return bytes_read; +} +/* }}} */ + +/* {{{ libssh2_channel_write_ex + * Send data to a channel + */ +LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *packet; + unsigned long packet_len, bufwrote = 0; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id); +#endif + if (channel->local.close) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0); + return -1; + } + + if (channel->local.eof) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0); + } + + if (!channel->blocking && (channel->local.window_size <= 0)) { + /* Can't write anything */ + return 0; + } + + packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */ + packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0); + return -1; + } + + while (buflen > 0) { + size_t bufwrite = buflen; + unsigned char *s = packet; + + *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA; + libssh2_htonu32(s, channel->remote.id); s += 4; + if (stream_id) { + libssh2_htonu32(s, stream_id); s += 4; + } + + /* twiddle our thumbs until there's window space available */ + while (channel->local.window_size <= 0) { + /* Don't worry -- This is never hit unless it's a blocking channel anyway */ + if (libssh2_packet_read(session, 1) < 0) { + /* Error occured, disconnect? */ + return -1; + } + } + + /* Don't exceed the remote end's limits */ + /* REMEMBER local means local as the SOURCE of the data */ + if (bufwrite > channel->local.window_size) { +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte window_size on %lu/%lu/%d", channel->local.window_size, channel->local.id, channel->remote.id, stream_id); +#endif + bufwrite = channel->local.window_size; + } + if (bufwrite > channel->local.packet_size) { +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte packet_size on %lu/%lu/%d", channel->local.packet_size, channel->local.id, channel->remote.id, stream_id); +#endif + bufwrite = channel->local.packet_size; + } + libssh2_htonu32(s, bufwrite); s += 4; + memcpy(s, buf, bufwrite); s += bufwrite; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id); +#endif + if (libssh2_packet_write(session, packet, s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + /* Shrink local window size */ + channel->local.window_size -= bufwrite; + + /* Adjust buf for next iteration */ + buflen -= bufwrite; + buf += bufwrite; + bufwrote += bufwrite; + + if (!channel->blocking) { + break; + } + } + + LIBSSH2_FREE(session, packet); + + return bufwrote; +} +/* }}} */ + +/* {{{ libssh2_channel_send_eof + * Send EOF on channel + */ +LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char packet[5]; /* packet_type(1) + channelno(4) */ + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu",channel->local.id, channel->remote.id); +#endif + packet[0] = SSH_MSG_CHANNEL_EOF; + libssh2_htonu32(packet + 1, channel->remote.id); + if (libssh2_packet_write(session, packet, 5)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send EOF on channel", 0); + return -1; + } + channel->local.eof = 1; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_channel_eof + * Read channel's eof status + */ +LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_PACKET *packet = session->packets.head; + + while (packet) { + if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) && + (channel->local.id == libssh2_ntohu32(packet->data + 1))) { + /* There's data waiting to be read yet, mask the EOF status */ + return 0; + } + packet = packet->next; + } + + return channel->remote.eof; +} +/* }}} */ + +/* {{{ libssh2_channel_close + * Close a channel + */ +LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char packet[5]; + + if (channel->local.close) { + /* Already closed, act like we sent another close, even though we didn't... shhhhhh */ + return 0; + } + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + + if (channel->close_cb) { + LIBSSH2_CHANNEL_CLOSE(session, channel); + } + channel->local.close = 1; + + packet[0] = SSH_MSG_CHANNEL_CLOSE; + libssh2_htonu32(packet + 1, channel->remote.id); + if (libssh2_packet_write(session, packet, 5)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send close-channel request", 0); + return -1; + } + + /* TODO: Wait up to a timeout value for a CHANNEL_CLOSE to come back, to avoid the problem alluded to in channel_nextid */ + + return 0; +} +/* }}} */ + +/* {{{ libssh2_channel_wait_closed + * Awaiting channel close after EOF + */ +LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION* session = channel->session; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + + /* while channel is not closed, read more + * packets from the network. + * Either or channel will be closed + * or network timeout will occur + */ + while (!channel->remote.close && libssh2_packet_read(session, 1) > 0) + ; + + return 1; +} +/* }}} */ + + +/* {{{ libssh2_channel_free + * Make sure a channel is closed, then remove the channel from the session and free its resource(s) + */ +LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char channel_id[4], *data; + unsigned long data_len; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Freeing channel %lu/%lu resources", channel->local.id, channel->remote.id); +#endif + /* Allow channel freeing even when the socket has lost its connection */ + if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) && + libssh2_channel_close(channel)) { + return -1; + } + + /* channel->remote.close *might* not be set yet, Well... + * We've sent the close packet, what more do you want? + * Just let packet_add ignore it when it finally arrives + */ + + /* Clear out packets meant for this channel */ + libssh2_htonu32(channel_id, channel->local.id); + while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) || + (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) { + LIBSSH2_FREE(session, data); + } + + /* free "channel_type" */ + if (channel->channel_type) { + LIBSSH2_FREE(session, channel->channel_type); + } + + /* Unlink from channel brigade */ + if (channel->prev) { + channel->prev->next = channel->next; + } else { + session->channels.head = channel->next; + } + if (channel->next) { + channel->next->prev = channel->prev; + } else { + session->channels.tail = channel->prev; + } + + LIBSSH2_FREE(session, channel); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_channel_window_read_ex + * Check the status of the read window + * Returns the number of bytes which the remote end may send without overflowing the window limit + * read_avail (if passed) will be populated with the number of bytes actually available to be read + * window_size_initial (if passed) will be populated with the window_size_initial as defined by the channel_open request + */ +LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial) +{ + if (window_size_initial) { + *window_size_initial = channel->remote.window_size_initial; + } + + if (read_avail) { + unsigned long bytes_queued = 0; + LIBSSH2_PACKET *packet = channel->session->packets.head; + + while (packet) { + unsigned char packet_type = packet->data[0]; + + if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) && + (libssh2_ntohu32(packet->data + 1) == channel->local.id)) { + bytes_queued += packet->data_len - packet->data_head; + } + + packet = packet->next; + } + + *read_avail = bytes_queued; + } + + return channel->remote.window_size; +} +/* }}} */ + +/* {{{ libssh2_channel_window_write_ex + * Check the status of the write window + * Returns the number of bytes which may be safely writen on the channel without blocking + * window_size_initial (if passed) will be populated with the size of the initial window as defined by the channel_open request + */ +LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial) +{ + if (window_size_initial) { + /* For locally initiated channels this is very often 0, so it's not *that* useful as information goes */ + *window_size_initial = channel->local.window_size_initial; + } + + return channel->local.window_size; +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/libssh2/comp.c b/pandora_agents/win32/ssh/libssh2/comp.c new file mode 100644 index 0000000000..e00f6c9010 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/comp.c @@ -0,0 +1,286 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include + +/* ******** + * none * + ******** */ + +/* {{{ libssh2_comp_method_none_comp + * Minimalist compression: Absolutely none + */ +static int libssh2_comp_method_none_comp(LIBSSH2_SESSION *session, int compress, + unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest, + const unsigned char *src, unsigned long src_len, void **abstract) +{ + *dest = (unsigned char *)src; + *dest_len = src_len; + + *free_dest = 0; + + return 0; +} +/* }}} */ + +static LIBSSH2_COMP_METHOD libssh2_comp_method_none = { + "none", + NULL, + libssh2_comp_method_none_comp, + NULL +}; + +#ifdef LIBSSH2_HAVE_ZLIB +/* ******** + * zlib * + ******** */ + +/* {{{ Memory management wrappers + * Yes, I realize we're doing a callback to a callback, + * Deal... + */ + +static voidpf libssh2_comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size) +{ + LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)opaque; + + return (voidpf)LIBSSH2_ALLOC(session, items * size); +} + +static void libssh2_comp_method_zlib_free(voidpf opaque, voidpf address) +{ + LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)opaque; + + LIBSSH2_FREE(session, address); +} +/* }}} */ + +/* {{{ libssh2_comp_method_zlib_init + * All your bandwidth are belong to us (so save some) + */ +static int libssh2_comp_method_zlib_init(LIBSSH2_SESSION *session, int compress, void **abstract) +{ + z_stream *strm; + int status; + + strm = (z_stream *) LIBSSH2_ALLOC(session, sizeof(z_stream)); + if (!strm) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for zlib compression/decompression", 0); + return -1; + } + memset(strm, 0, sizeof(z_stream)); + + strm->opaque = (voidpf)session; + strm->zalloc = (alloc_func)libssh2_comp_method_zlib_alloc; + strm->zfree = (free_func)libssh2_comp_method_zlib_free; + if (compress) { + /* deflate */ + status = deflateInit(strm, Z_DEFAULT_COMPRESSION); + } else { + /* inflate */ + status = inflateInit(strm); + } + + if (status != Z_OK) { + LIBSSH2_FREE(session, strm); + return -1; + } + *abstract = strm; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_comp_method_zlib_comp + * zlib, a compression standard for all occasions + */ +static int libssh2_comp_method_zlib_comp(LIBSSH2_SESSION *session, int compress, + unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest, + const unsigned char *src, unsigned long src_len, void **abstract) +{ + z_stream *strm = (z_stream *) *abstract; + /* A short-term alloc of a full data chunk is better than a series of reallocs */ + char *out; + int out_maxlen = compress ? (src_len + 4) : (2 * src_len); + int limiter = 0; + + /* In practice they never come smaller than this */ + if (out_maxlen < 25) { + out_maxlen = 25; + } + + if (out_maxlen > (int) payload_limit) { + out_maxlen = payload_limit; + } + + strm->next_in = (Bytef *)src; + strm->avail_in = src_len; + strm->next_out = (Bytef *) LIBSSH2_ALLOC(session, out_maxlen); + out = (char *) strm->next_out; + strm->avail_out = out_maxlen; + if (!strm->next_out) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate compression/decompression buffer", 0); + return -1; + } + while (strm->avail_in) { + int status; + + if (compress) { + status = deflate(strm, Z_PARTIAL_FLUSH); + } else { + status = inflate(strm, Z_PARTIAL_FLUSH); + } + if (status != Z_OK) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compress/decompression failure", 0); + LIBSSH2_FREE(session, out); + return -1; + } + if (strm->avail_in) { + unsigned long out_ofs = out_maxlen - strm->avail_out; + + out_maxlen += compress ? (strm->avail_in + 4) : (2 * strm->avail_in); + + if ((out_maxlen > (int) payload_limit) && !compress && limiter++) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, "Excessive growth in decompression phase", 0); + LIBSSH2_FREE(session, out); + return -1; + } + + out = (char *) LIBSSH2_REALLOC(session, out, out_maxlen); + if (!out) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to expand compress/decompression buffer", 0); + return -1; + } + strm->next_out = (Bytef *) out + out_ofs; + strm->avail_out += compress ? (strm->avail_in + 4) : (2 * strm->avail_in); + } else while (!strm->avail_out) { + /* Done with input, might be a byte or two in internal buffer during compress + * Or potentially many bytes if it's a decompress + */ + int grow_size = compress ? 8 : 1024; + + if (out_maxlen >= (int) payload_limit) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, "Excessive growth in decompression phase", 0); + LIBSSH2_FREE(session, out); + return -1; + } + + if (grow_size > (int) (payload_limit - out_maxlen)) { + grow_size = payload_limit - out_maxlen; + } + + out_maxlen += grow_size; + strm->avail_out = grow_size; + + out = (char *) LIBSSH2_REALLOC(session, out, out_maxlen); + if (!out) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to expand final compress/decompress buffer", 0); + return -1; + } + strm->next_out = (Bytef *) out + out_maxlen - grow_size; + + if (compress) { + status = deflate(strm, Z_PARTIAL_FLUSH); + } else { + status = inflate(strm, Z_PARTIAL_FLUSH); + } + if (status != Z_OK) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compress/decompression failure", 0); + LIBSSH2_FREE(session, out); + return -1; + } + } + } + + *dest = (unsigned char *) out; + *dest_len = out_maxlen - strm->avail_out; + *free_dest = 1; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_comp_method_zlib_dtor + * All done, no more compression for you + */ +static int libssh2_comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compress, void **abstract) +{ + z_stream *strm = (z_stream *) *abstract; + + if (strm) { + if (compress) { + /* deflate */ + deflateEnd(strm); + } else { + /* inflate */ + inflateEnd(strm); + } + + LIBSSH2_FREE(session, strm); + } + + *abstract = NULL; + + return 0; +} +/* }}} */ + +static LIBSSH2_COMP_METHOD libssh2_comp_method_zlib = { + "zlib", + libssh2_comp_method_zlib_init, + libssh2_comp_method_zlib_comp, + libssh2_comp_method_zlib_dtor, +}; +#endif /* LIBSSH2_HAVE_ZLIB */ + +/* *********************** + * Compression Methods * + *********************** */ + +static LIBSSH2_COMP_METHOD *_libssh2_comp_methods[] = { + &libssh2_comp_method_none, +#ifdef LIBSSH2_HAVE_ZLIB + &libssh2_comp_method_zlib, +#endif /* LIBSSH2_HAVE_ZLIB */ + NULL +}; + +LIBSSH2_COMP_METHOD **libssh2_comp_methods(void) { + return _libssh2_comp_methods; +} + diff --git a/pandora_agents/win32/ssh/libssh2/crypt.c b/pandora_agents/win32/ssh/libssh2/crypt.c new file mode 100644 index 0000000000..11d6665572 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/crypt.c @@ -0,0 +1,189 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include + +#ifdef LIBSSH2_CRYPT_NONE +/* {{{ libssh2_crypt_none_crypt + * Minimalist cipher: VERY secure *wink* + */ +static int libssh2_crypt_none_crypt(LIBSSH2_SESSION *session, unsigned char *buf, void **abstract) +{ + /* Do nothing to the data! */ + return 0; +} +/* }}} */ + +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = { + "none", + 8, /* blocksize (SSH2 defines minimum blocksize as 8) */ + 0, /* iv_len */ + 0, /* secret_len */ + 0, /* flags */ + NULL, + libssh2_crypt_none_crypt, + NULL +}; +#endif + +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = { + "3des-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 24, /* secret length */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **)) EVP_des_ede3_cbc, + NULL, +}; + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = { + "aes128-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 16, /* secret length -- 16*8 == 128bit */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_aes_128_cbc, + NULL, +}; + +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = { + "aes192-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 24, /* secret length -- 24*8 == 192bit */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_aes_192_cbc, + NULL, +}; + +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = { + "aes256-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 32, /* secret length -- 32*8 == 256bit */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_aes_256_cbc, + NULL, +}; + +/* rijndael-cbc@lysator.liu.se == aes256-cbc */ +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_rijndael_cbc_lysator_liu_se = { + "rijndael-cbc@lysator.liu.se", + 16, /* blocksize */ + 16, /* initial value length */ + 32, /* secret length -- 32*8 == 256bit */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_aes_256_cbc, + NULL, +}; +#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)*/ + +#ifndef OPENSSL_NO_BLOWFISH +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = { + "blowfish-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_bf_cbc, + NULL, +}; +#endif /* ! OPENSSL_NO_BLOWFISH */ + +#ifndef OPENSSL_NO_CAST +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = { + "cast128-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_cast5_cbc, + NULL, +}; +#endif /* ! OPENSSL_NO_CAST */ + +#ifndef OPENSSL_NO_RC4 +static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = { + "arcfour", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + LIBSSH2_CRYPT_METHOD_FLAG_EVP, + NULL, + (int (*)(LIBSSH2_SESSION *, unsigned char *, void **))EVP_rc4, + NULL, +}; +#endif /* ! OPENSSL_NO_RC4 */ + +static LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = { +#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) + &libssh2_crypt_method_aes256_cbc, + &libssh2_crypt_method_rijndael_cbc_lysator_liu_se, /* == aes256-cbc */ + &libssh2_crypt_method_aes192_cbc, + &libssh2_crypt_method_aes128_cbc, +#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) */ +#ifndef OPENSSL_NO_BLOWFISH + &libssh2_crypt_method_blowfish_cbc, +#endif /* ! OPENSSL_NO_BLOWFISH */ +#ifndef OPENSSL_NO_RC4 + &libssh2_crypt_method_arcfour, +#endif /* ! OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_CAST + &libssh2_crypt_method_cast128_cbc, +#endif /* ! OPENSSL_NO_CAST */ +#ifndef OPENSSL_NO_DES + &libssh2_crypt_method_3des_cbc, +#endif /* ! OPENSSL_NO_DES */ +#ifdef LIBSSH2_CRYPT_NONE + &libssh2_crypt_method_none, +#endif + NULL +}; + +/* Expose to kex.c */ +LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void) { + return _libssh2_crypt_methods; +} diff --git a/pandora_agents/win32/ssh/libssh2/hostkey.c b/pandora_agents/win32/ssh/libssh2/hostkey.c new file mode 100644 index 0000000000..64b7fc38a1 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/hostkey.c @@ -0,0 +1,546 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#include +#include +#include + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifndef OPENSSL_NO_RSA +/* *********** + * ssh-rsa * + *********** */ + +static int libssh2_hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION *session, void **abstract); + +/* {{{ libssh2_hostkey_method_ssh_rsa_init + * Initialize the server hostkey working area with e/n pair + */ +static int libssh2_hostkey_method_ssh_rsa_init(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract) +{ + RSA *rsactx; + unsigned char *s, *e, *n; + unsigned long len, e_len, n_len; + + if (*abstract) { + libssh2_hostkey_method_ssh_rsa_dtor(session, abstract); + *abstract = NULL; + } + + s = hostkey_data; + len = libssh2_ntohu32(s); s += 4; + if (len != 7 || strncmp((char *) s, "ssh-rsa", 7) != 0) { + return -1; + } s += 7; + + e_len = libssh2_ntohu32(s); s += 4; + e = s; s += e_len; + n_len = libssh2_ntohu32(s); s += 4; + n = s; s += n_len; + + rsactx = RSA_new(); + rsactx->e = BN_new(); + BN_bin2bn(e, e_len, rsactx->e); + rsactx->n = BN_new(); + BN_bin2bn(n, n_len, rsactx->n); + + *abstract = rsactx; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_passphrase_cb + * TODO: Optionally call a passphrase callback specified by the calling program + */ +static int libssh2_hostkey_method_ssh_rsadsa_passphrase_cb(char *buf, int size, int rwflag, char *passphrase) +{ + int passphrase_len = strlen(passphrase); + + if (passphrase_len > (size - 1)) { + passphrase_len = size - 1; + } + memcpy(buf, passphrase, passphrase_len); + buf[passphrase_len] = '\0'; + + return passphrase_len; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_initPEM + * Load a Private Key from a PEM file + */ +static int libssh2_hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract) +{ + RSA *rsactx; + FILE *fp; + + if (*abstract) { + libssh2_hostkey_method_ssh_rsa_dtor(session, abstract); + *abstract = NULL; + } + + fp = fopen((char *)privkeyfile, "r"); + if (!fp) { + return -1; + } + + if (!EVP_get_cipherbyname("des")) { + /* If this cipher isn't loaded it's a pretty good indication that none are. + * I have *NO DOUBT* that there's a better way to deal with this ($#&%#$(%$#( + * Someone buy me an OpenSSL manual and I'll read up on it. + */ + OpenSSL_add_all_ciphers(); + } + rsactx = PEM_read_RSAPrivateKey(fp, NULL, (int (*) (char *, int, int, void *))libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, (void*)passphrase); + if (!rsactx) { + fclose(fp); + return -1; + } + fclose(fp); + + *abstract = rsactx; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_sign + * Verify signature created by remote + */ +static int libssh2_hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len, + const unsigned char *m, unsigned long m_len, void **abstract) +{ + RSA *rsactx = (RSA*)(*abstract); + unsigned char hash[SHA_DIGEST_LENGTH]; + int ret; + + /* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */ + sig += 15; sig_len -= 15; + SHA1(m, m_len, hash); + ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned char *)sig, sig_len, rsactx); + + return (ret == 1) ? 0 : -1; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_sign + * Sign data to send to remote + */ +static int libssh2_hostkey_method_ssh_rsa_sign(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, + const unsigned char *buf, unsigned long buf_len, void **abstract) +{ + RSA *rsactx = (RSA*)(*abstract); + int ret; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + char *sig; + int sig_len; + + sig_len = RSA_size(rsactx); + sig = (char *) LIBSSH2_ALLOC(session, sig_len); + + if (!sig) { + return -1; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, buf, buf_len); + SHA1_Final(hash, &ctx); + + ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned char *) sig, (unsigned int *) &sig_len, rsactx); + if (!ret) { + LIBSSH2_FREE(session, sig); + return -1; + } + + *signature = (unsigned char *) sig; + *signature_len = sig_len; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_signv + * Construct a signature from an array of vectors + */ +static int libssh2_hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, + unsigned long veccount, const struct iovec datavec[], void **abstract) +{ + RSA *rsactx = (RSA*)(*abstract); + int ret, i; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + char *sig; + int sig_len; + + sig_len = RSA_size(rsactx); + sig = (char *) LIBSSH2_ALLOC(session, sig_len); + + if (!sig) { + return -1; + } + + SHA1_Init(&ctx); + for(i = 0; i < (int) veccount; i++) { + SHA1_Update(&ctx, datavec[i].iov_base, datavec[i].iov_len); + } + SHA1_Final(hash, &ctx); + + ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, (unsigned char *) sig, (unsigned int *) &sig_len, rsactx); + + if (!ret) { + LIBSSH2_FREE(session, sig); + return -1; + } + + *signature = (unsigned char *) sig; + *signature_len = sig_len; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_dtor + * Shutdown the hostkey + */ +static int libssh2_hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION *session, void **abstract) +{ + RSA *rsactx = (RSA*)(*abstract); + + RSA_free(rsactx); + + *abstract = NULL; + + return 0; +} +/* }}} */ + +static LIBSSH2_HOSTKEY_METHOD libssh2_hostkey_method_ssh_rsa = { + "ssh-rsa", + MD5_DIGEST_LENGTH, + libssh2_hostkey_method_ssh_rsa_init, + libssh2_hostkey_method_ssh_rsa_initPEM, + libssh2_hostkey_method_ssh_rsa_sig_verify, + libssh2_hostkey_method_ssh_rsa_sign, + libssh2_hostkey_method_ssh_rsa_signv, + NULL, /* encrypt */ + libssh2_hostkey_method_ssh_rsa_dtor, +}; +#endif /* ! OPENSSL_NO_RSA */ + +#ifndef OPENSSL_NO_DSA +/* *********** + * ssh-dss * + *********** */ + +static int libssh2_hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION *session, void **abstract); + +/* {{{ libssh2_hostkey_method_ssh_dss_init + * Initialize the server hostkey working area with p/q/g/y set + */ +static int libssh2_hostkey_method_ssh_dss_init(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract) +{ + DSA *dsactx; + unsigned char *p, *q, *g, *y, *s; + unsigned long p_len, q_len, g_len, y_len, len; + + if (*abstract) { + libssh2_hostkey_method_ssh_dss_dtor(session, abstract); + *abstract = NULL; + } + + s = hostkey_data; + len = libssh2_ntohu32(s); s += 4; + if (len != 7 || strncmp((char *) s, "ssh-dss", 7) != 0) { + return -1; + } s += 7; + + p_len = libssh2_ntohu32(s); s += 4; + p = s; s += p_len; + q_len = libssh2_ntohu32(s); s += 4; + q = s; s += q_len; + g_len = libssh2_ntohu32(s); s += 4; + g = s; s += g_len; + y_len = libssh2_ntohu32(s); s += 4; + y = s; s += y_len; + + dsactx = DSA_new(); + dsactx->p = BN_new(); + BN_bin2bn(p, p_len, dsactx->p); + dsactx->q = BN_new(); + BN_bin2bn(q, q_len, dsactx->q); + dsactx->g = BN_new(); + BN_bin2bn(g, g_len, dsactx->g); + dsactx->pub_key = BN_new(); + BN_bin2bn(y, y_len, dsactx->pub_key); + + *abstract = dsactx; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_dss_initPEM + * Load a Private Key from a PEM file + */ +static int libssh2_hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract) +{ + DSA *dsactx; + FILE *fp; + + if (*abstract) { + libssh2_hostkey_method_ssh_dss_dtor(session, abstract); + *abstract = NULL; + } + + fp = fopen((char *) privkeyfile, "r"); + if (!fp) { + return -1; + } + + if (!EVP_get_cipherbyname("des")) { + /* If this cipher isn't loaded it's a pretty good indication that none are. + * I have *NO DOUBT* that there's a better way to deal with this ($#&%#$(%$#( + * Someone buy me an OpenSSL manual and I'll read up on it. + */ + OpenSSL_add_all_ciphers(); + } + dsactx = PEM_read_DSAPrivateKey(fp, NULL, (int (*) (char *, int, int, void *))libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, (void*)passphrase); + if (!dsactx) { + fclose(fp); + return -1; + } + fclose(fp); + + *abstract = dsactx; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_dss_sign + * Verify signature created by remote + */ +static int libssh2_hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len, + const unsigned char *m, unsigned long m_len, void **abstract) +{ + DSA *dsactx = (DSA*)(*abstract); + unsigned char hash[SHA_DIGEST_LENGTH]; + DSA_SIG dsasig; + int ret; + + /* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */ + sig += 15; sig_len -= 15; + if (sig_len != 40) { + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid DSS signature length", 0); + return -1; + } + dsasig.r = BN_new(); + BN_bin2bn(sig, 20, dsasig.r); + dsasig.s = BN_new(); + BN_bin2bn(sig + 20, 20, dsasig.s); + + SHA1(m, m_len, hash); + ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, &dsasig, dsactx); + + return (ret == 1) ? 0 : -1; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_dss_sign + * Sign data to send to remote + */ +static int libssh2_hostkey_method_ssh_dss_sign(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, + const unsigned char *buf, unsigned long buf_len, void **abstract) +{ + DSA *dsactx = (DSA*)(*abstract); + DSA_SIG *sig; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + + *signature = (unsigned char *) LIBSSH2_ALLOC(session, 2 * SHA_DIGEST_LENGTH); + *signature_len = 2 * SHA_DIGEST_LENGTH; + + if (!(*signature)) { + return -1; + } + + SHA1_Init(&ctx); + SHA1_Update(&ctx, buf, buf_len); + SHA1_Final(hash, &ctx); + + sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); + if (!sig) { + LIBSSH2_FREE(session, *signature); + return -1; + } + + BN_bn2bin(sig->r, *signature); + BN_bn2bin(sig->s, *signature + SHA_DIGEST_LENGTH); + + DSA_SIG_free(sig); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_dss_signv + * Construct a signature from an array of vectors + */ +static int libssh2_hostkey_method_ssh_dss_signv(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, + unsigned long veccount, const struct iovec datavec[], void **abstract) +{ + DSA *dsactx = (DSA*)(*abstract); + DSA_SIG *sig; + unsigned char hash[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + int r_len, s_len, rs_pad, i; + + *signature = (unsigned char *) LIBSSH2_ALLOC(session, 2 * SHA_DIGEST_LENGTH); + *signature_len = 2 * SHA_DIGEST_LENGTH; + memset(*signature, 0, 2 * SHA_DIGEST_LENGTH); + + if (!(*signature)) { + return -1; + } + + SHA1_Init(&ctx); + for(i = 0; i < (int) veccount; i++) { + SHA1_Update(&ctx, datavec[i].iov_base, datavec[i].iov_len); + } + SHA1_Final(hash, &ctx); + + sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); + if (!sig) { + LIBSSH2_FREE(session, *signature); + return -1; + } + + r_len = BN_num_bytes(sig->r); + s_len = BN_num_bytes(sig->s); + rs_pad = (2 * SHA_DIGEST_LENGTH) - (r_len + s_len); + if (rs_pad < 0) { + DSA_SIG_free(sig); + LIBSSH2_FREE(session, *signature); + return -1; + } + + BN_bn2bin(sig->r, *signature + rs_pad); + BN_bn2bin(sig->s, *signature + rs_pad + r_len); + + DSA_SIG_free(sig); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_dss_dtor + * Shutdown the hostkey method + */ +static int libssh2_hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION *session, void **abstract) +{ + DSA *dsactx = (DSA*)(*abstract); + + DSA_free(dsactx); + + *abstract = NULL; + + return 0; +} +/* }}} */ + +static LIBSSH2_HOSTKEY_METHOD libssh2_hostkey_method_ssh_dss = { + "ssh-dss", + MD5_DIGEST_LENGTH, + libssh2_hostkey_method_ssh_dss_init, + libssh2_hostkey_method_ssh_dss_initPEM, + libssh2_hostkey_method_ssh_dss_sig_verify, + libssh2_hostkey_method_ssh_dss_sign, + libssh2_hostkey_method_ssh_dss_signv, + NULL, /* encrypt */ + libssh2_hostkey_method_ssh_dss_dtor, +}; +#endif /* ! OPENSSL_NO_DSA */ + +static LIBSSH2_HOSTKEY_METHOD *_libssh2_hostkey_methods[] = { +#ifndef OPENSSL_NO_RSA + &libssh2_hostkey_method_ssh_rsa, +#endif /* ! OPENSSL_NO_RSA */ +#ifndef OPENSSL_NO_DSA + &libssh2_hostkey_method_ssh_dss, +#endif /* ! OPENSSL_NO_DSA */ + NULL +}; + +LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void) +{ + return _libssh2_hostkey_methods; +} + +/* {{{ libssh2_hostkey_hash + * Returns hash signature + * Returned buffer should NOT be freed + * Length of buffer is determined by hash type + * i.e. MD5 == 16, SHA1 == 20 + */ +LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type) +{ + switch (hash_type) { +#ifndef OPENSSL_NO_MD5 + case LIBSSH2_HOSTKEY_HASH_MD5: + return (char *) session->server_hostkey_md5; + break; +#endif /* ! OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + case LIBSSH2_HOSTKEY_HASH_SHA1: + return (char *) session->server_hostkey_sha1; + break; +#endif /* ! OPENSSL_NO_SHA */ + default: + return NULL; + } +} +/* }}} */ + + diff --git a/pandora_agents/win32/ssh/libssh2/kex.c b/pandora_agents/win32/ssh/libssh2/kex.c new file mode 100644 index 0000000000..1db5a430db --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/kex.c @@ -0,0 +1,1382 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#include +#include +#include + +/* TODO: Switch this to an inline and handle alloc() failures */ +/* Helper macro called from libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange */ +#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ +{ \ + SHA_CTX hash; \ + unsigned long len = 0; \ + if (!(value)) { \ + value = (unsigned char *) LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \ + } \ + while (len < (unsigned long) reqlen) { \ + SHA1_Init(&hash); \ + SHA1_Update(&hash, k_value, k_value_len); \ + SHA1_Update(&hash, h_sig_comp, SHA_DIGEST_LENGTH); \ + if (len > 0) { \ + SHA1_Update(&hash, (unsigned char *) value, len); \ + } else { \ + SHA1_Update(&hash, (version), 1); \ + SHA1_Update(&hash, session->session_id, session->session_id_len); \ + } \ + SHA1_Final((value) + len, &hash); \ + len += SHA_DIGEST_LENGTH; \ + } \ +} + +/* {{{ libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange + * Diffie Hellman Key Exchange, Group Agnostic + */ +static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, BIGNUM *g, BIGNUM *p, int group_order, + unsigned char packet_type_init, unsigned char packet_type_reply, + unsigned char *midhash, unsigned long midhash_len) +{ + unsigned char *e_packet = NULL, *s_packet = NULL, *tmp, h_sig_comp[SHA_DIGEST_LENGTH], c; + unsigned long e_packet_len, s_packet_len, tmp_len; + int ret = 0; + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *x = BN_new(); /* Random from client */ + BIGNUM *e = BN_new(); /* g^x mod p */ + BIGNUM *f = BN_new(); /* g^(Random from server) mod p */ + BIGNUM *k = BN_new(); /* The shared secret: f^x mod p */ + unsigned char *s, *f_value, *k_value = NULL, *h_sig; + unsigned long f_value_len, k_value_len, h_sig_len; + SHA_CTX exchange_hash; + + /* Generate x and e */ + BN_rand(x, group_order, 0, -1); + BN_mod_exp(e, g, x, p, ctx); + + /* Send KEX init */ + e_packet_len = BN_num_bytes(e) + 6; /* packet_type(1) + String Length(4) + leading 0(1) */ + if (BN_num_bits(e) % 8) { + /* Leading 00 not needed */ + e_packet_len--; + } + e_packet = (unsigned char *) LIBSSH2_ALLOC(session, e_packet_len); + if (!e_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Out of memory error", 0); + ret = -1; + goto clean_exit; + } + e_packet[0] = packet_type_init; + libssh2_htonu32(e_packet + 1, e_packet_len - 5); + if (BN_num_bits(e) % 8) { + BN_bn2bin(e, e_packet + 5); + } else { + e_packet[5] = 0; + BN_bn2bin(e, e_packet + 6); + } + +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init); +#endif + if (libssh2_packet_write(session, e_packet, e_packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0); + ret = -11; + goto clean_exit; + } + + /* Wait for KEX reply */ + if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0); + ret = -1; + goto clean_exit; + } + + /* Parse KEXDH_REPLY */ + s = s_packet + 1; + + session->server_hostkey_len = libssh2_ntohu32(s); s += 4; + session->server_hostkey = (unsigned char *) LIBSSH2_ALLOC(session, session->server_hostkey_len); + if (!session->server_hostkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for a copy of the host key", 0); + ret = -1; + goto clean_exit; + } + memcpy(session->server_hostkey, s, session->server_hostkey_len); + s += session->server_hostkey_len; + +#ifndef OPENSSL_NO_MD5 +{ + MD5_CTX fingerprint_ctx; + + MD5_Init(&fingerprint_ctx); + MD5_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); + MD5_Final(session->server_hostkey_md5, &fingerprint_ctx); +} +#ifdef LIBSSH2_DEBUG_KEX +{ + char fingerprint[50], *fprint = fingerprint; + int i; + for(i = 0; i < 16; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's MD5 Fingerprint: %s", fingerprint); +} +#endif /* LIBSSH2_DEBUG_KEX */ +#endif /* ! OPENSSL_NO_MD5 */ + +#ifndef OPENSSL_NO_SHA +{ + SHA_CTX fingerprint_ctx; + + SHA1_Init(&fingerprint_ctx); + SHA1_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len); + SHA1_Final(session->server_hostkey_sha1, &fingerprint_ctx); +} +#ifdef LIBSSH2_DEBUG_KEX +{ + char fingerprint[64], *fprint = fingerprint; + int i; + for(i = 0; i < 20; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint); +} +#endif /* LIBSSH2_DEBUG_KEX */ +#endif /* ! OPENSSL_NO_SHA */ + + if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0); + ret = -1; + goto clean_exit; + } + + f_value_len = libssh2_ntohu32(s); s += 4; + f_value = s; s += f_value_len; + BN_bin2bn(f_value, f_value_len, f); + + h_sig_len = libssh2_ntohu32(s); s += 4; + h_sig = s; + + /* Compute the shared secret */ + BN_mod_exp(k, f, x, p, ctx); + k_value_len = BN_num_bytes(k) + 5; + if (BN_num_bits(k) % 8) { + /* don't need leading 00 */ + k_value_len--; + } + k_value = (unsigned char *) LIBSSH2_ALLOC(session, k_value_len); + if (!k_value) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate buffer for K", 0); + ret = -1; + goto clean_exit; + } + libssh2_htonu32(k_value, k_value_len - 4); + if (BN_num_bits(k) % 8) { + BN_bn2bin(k, k_value + 4); + } else { + k_value[4] = 0; + BN_bn2bin(k, k_value + 5); + } + + SHA1_Init(&exchange_hash); + if (session->local.banner) { + libssh2_htonu32(h_sig_comp, strlen((char *) session->local.banner) - 2); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, session->local.banner, strlen((char *) session->local.banner) - 2); + } else { + libssh2_htonu32(h_sig_comp, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, LIBSSH2_SSH_DEFAULT_BANNER, sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + } + + libssh2_htonu32(h_sig_comp, strlen((char *)session->remote.banner)); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, session->remote.banner, strlen((char *) session->remote.banner)); + + libssh2_htonu32(h_sig_comp, session->local.kexinit_len); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, session->local.kexinit, session->local.kexinit_len); + + libssh2_htonu32(h_sig_comp, session->remote.kexinit_len); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, session->remote.kexinit, session->remote.kexinit_len); + + libssh2_htonu32(h_sig_comp, session->server_hostkey_len); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, session->server_hostkey, session->server_hostkey_len); + + if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { + /* diffie-hellman-group-exchange hashes additional fields */ +#ifdef LIBSSH2_DH_GEX_NEW + libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(h_sig_comp + 4, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(h_sig_comp + 8, LIBSSH2_DH_GEX_MAXGROUP); + SHA1_Update(&exchange_hash, h_sig_comp, 12); +#else + libssh2_htonu32(h_sig_comp, LIBSSH2_DH_GEX_OPTGROUP); + SHA1_Update(&exchange_hash, h_sig_comp, 4); +#endif + } + + if (midhash) { + SHA1_Update(&exchange_hash, midhash, midhash_len); + } + + SHA1_Update(&exchange_hash, e_packet + 1, e_packet_len - 1); + + libssh2_htonu32(h_sig_comp, f_value_len); + SHA1_Update(&exchange_hash, h_sig_comp, 4); + SHA1_Update(&exchange_hash, f_value, f_value_len); + + SHA1_Update(&exchange_hash, k_value, k_value_len); + + SHA1_Final(h_sig_comp, &exchange_hash); + + if (session->hostkey->sig_verify(session, h_sig, h_sig_len, h_sig_comp, 20, &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, "Unable to verify hostkey signature", 0); + ret = -1; + goto clean_exit; + } + +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message"); +#endif + c = SSH_MSG_NEWKEYS; + if (libssh2_packet_write(session, &c, 1)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0); + ret = -1; + goto clean_exit; + } + + if (libssh2_packet_require(session, SSH_MSG_NEWKEYS, &tmp, &tmp_len)) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for NEWKEYS", 0); + ret = -1; + goto clean_exit; + } + /* The first key exchange has been performed, switch to active crypt/comp/mac mode */ + session->state |= LIBSSH2_STATE_NEWKEYS; +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message"); +#endif + + /* This will actually end up being just packet_type(1) for this packet type anyway */ + LIBSSH2_FREE(session, tmp); + + if (!session->session_id) { + session->session_id = (unsigned char *) LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); + if (!session->session_id) { + ret = -1; + goto clean_exit; + } + memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH); + session->session_id_len = SHA_DIGEST_LENGTH; +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated"); +#endif + } + + /* Calculate IV/Secret/Key for each direction */ + if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + if (session->local.crypt_abstract) { + LIBSSH2_FREE(session, session->local.crypt_abstract); + session->local.crypt_abstract = NULL; + } + } else { + if (session->local.crypt->dtor) { + /* Cleanup any existing cipher */ + session->local.crypt->dtor(session, &session->local.crypt_abstract); + } + } + + if (session->local.crypt->init || (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP)) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->local.crypt->iv_len, "A"); + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->local.crypt->secret_len, "C"); + if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + EVP_CIPHER *(*get_cipher)(void) = (EVP_CIPHER * (*) ())session->local.crypt->crypt; + EVP_CIPHER *cipher = get_cipher(); + EVP_CIPHER_CTX *ctx; + + ctx = (EVP_CIPHER_CTX *) LIBSSH2_ALLOC(session, sizeof(EVP_CIPHER_CTX)); + if (!ctx) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + EVP_CipherInit(ctx, cipher, secret, iv, 1); + session->local.crypt_abstract = ctx; + free_iv = 1; + free_secret = 1; + } else { + session->local.crypt->init(session, iv, &free_iv, secret, &free_secret, 1, &session->local.crypt_abstract); + } + + if (free_iv) { + memset(iv, 0, session->local.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->local.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } + } +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server IV and Key calculated"); +#endif + + if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + if (session->remote.crypt_abstract) { + LIBSSH2_FREE(session, session->remote.crypt_abstract); + session->remote.crypt_abstract = NULL; + } + } else { + if (session->remote.crypt->dtor) { + /* Cleanup any existing cipher */ + session->remote.crypt->dtor(session, &session->remote.crypt_abstract); + } + } + + if (session->remote.crypt->init || (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP)) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, session->remote.crypt->iv_len, "B"); + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, session->remote.crypt->secret_len, "D"); + if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + EVP_CIPHER *(*get_cipher)(void) = (EVP_CIPHER * (*) ()) session->remote.crypt->crypt; + EVP_CIPHER *cipher = get_cipher(); + EVP_CIPHER_CTX *ctx; + + ctx = (EVP_CIPHER_CTX *) LIBSSH2_ALLOC(session, sizeof(EVP_CIPHER_CTX)); + if (!ctx) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + EVP_CipherInit(ctx, cipher, secret, iv, 0); + session->remote.crypt_abstract = ctx; + free_iv = 1; + free_secret = 1; + } else { + session->remote.crypt->init(session, iv, &free_iv, secret, &free_secret, 0, &session->remote.crypt_abstract); + } + + if (free_iv) { + memset(iv, 0, session->remote.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->remote.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } + } +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated"); +#endif + + if (session->local.mac->dtor) { + session->local.mac->dtor(session, &session->local.mac_abstract); + } + + if (session->local.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->local.mac->key_len, "E"); + session->local.mac->init(session, key, &free_key, &session->local.mac_abstract); + + if (free_key) { + memset(key, 0, session->local.mac->key_len); + LIBSSH2_FREE(session, key); + } + } +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated"); +#endif + + if (session->remote.mac->dtor) { + session->remote.mac->dtor(session, &session->remote.mac_abstract); + } + + if (session->remote.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, session->remote.mac->key_len, "F"); + session->remote.mac->init(session, key, &free_key, &session->remote.mac_abstract); + + if (free_key) { + memset(key, 0, session->remote.mac->key_len); + LIBSSH2_FREE(session, key); + } + } +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated"); +#endif + + clean_exit: + BN_clear_free(x); + BN_clear_free(e); + BN_clear_free(f); + BN_clear_free(k); + BN_CTX_free(ctx); + + if (e_packet) { + LIBSSH2_FREE(session, e_packet); + } + + if (s_packet) { + LIBSSH2_FREE(session, s_packet); + } + + if (k_value) { + LIBSSH2_FREE(session, k_value); + } + + if (session->server_hostkey) { + LIBSSH2_FREE(session, session->server_hostkey); + session->server_hostkey = NULL; + } + + return ret; +} +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange + * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1 + */ +static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session) +{ + unsigned char p_value[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + /* g == 2 */ + BIGNUM *p = BN_new(); /* SSH2 defined value (p_value) */ + BIGNUM *g = BN_new(); /* SSH2 defined value (2) */ + int ret; + + /* Initialize P and G */ + BN_set_word(g, 2); + BN_bin2bn(p_value, 128, p); + +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group1 Key Exchange"); +#endif + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 128, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); + + BN_clear_free(p); + BN_clear_free(g); + + return ret; +} +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange + * Diffie-Hellman Group14 Key Exchange using SHA1 + */ +static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session) +{ + unsigned char p_value[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + /* g == 2 */ + BIGNUM *p = BN_new(); /* SSH2 defined value (p_value) */ + BIGNUM *g = BN_new(); /* SSH2 defined value (2) */ + int ret; + + /* Initialize P and G */ + BN_set_word(g, 2); + BN_bin2bn(p_value, 256, p); + +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group14 Key Exchange"); +#endif + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 256, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0); + + BN_clear_free(p); + BN_clear_free(g); + + return ret; +} +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange + * Diffie-Hellman Group Exchange Key Exchange using SHA1 + * Negotiates random(ish) group for secret derivation + */ +static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LIBSSH2_SESSION *session) +{ + unsigned char request[13], *s, *data; + unsigned long data_len, p_len, g_len, request_len; + BIGNUM *p = BN_new(); + BIGNUM *g = BN_new(); + int ret; + + /* Ask for a P and G pair */ +#ifdef LIBSSH2_DH_GEX_NEW + request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; + libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP); + request_len = 13; +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)"); +#endif +#else + request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; + libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP); + request_len = 5; +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)"); +#endif +#endif + + if (libssh2_packet_write(session, request, request_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send Group Exchange Request", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + if (libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timeout waiting for GEX_GROUP reply", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + s = data + 1; + p_len = libssh2_ntohu32(s); s += 4; + BN_bin2bn(s, p_len, p); s += p_len; + + g_len = libssh2_ntohu32(s); s += 4; + BN_bin2bn(s, g_len, g); s += g_len; + + ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, p_len, SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, data + 1, data_len - 1); + + LIBSSH2_FREE(session, data); + + dh_gex_clean_exit: + BN_clear_free(g); + BN_clear_free(p); + + return ret; +} +/* }}} */ + +#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001 +#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002 + +LIBSSH2_KEX_METHOD libssh2_kex_method_diffie_helman_group1_sha1 = { + "diffie-hellman-group1-sha1", + libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +LIBSSH2_KEX_METHOD libssh2_kex_method_diffie_helman_group14_sha1 = { + "diffie-hellman-group14-sha1", + libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +LIBSSH2_KEX_METHOD libssh2_kex_method_diffie_helman_group_exchange_sha1 = { + "diffie-hellman-group-exchange-sha1", + libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = { + &libssh2_kex_method_diffie_helman_group14_sha1, + &libssh2_kex_method_diffie_helman_group_exchange_sha1, + &libssh2_kex_method_diffie_helman_group1_sha1, + NULL +}; + +typedef struct _LIBSSH2_COMMON_METHOD { + char *name; +} LIBSSH2_COMMON_METHOD; + +/* {{{ libssh2_kex_method_strlen + * Calculate the length of a particular method list's resulting string + * Includes SUM(strlen() of each individual method plus 1 (for coma)) - 1 (because the last coma isn't used) + * Another sign of bad coding practices gone mad. Pretend you don't see this. + */ +static size_t libssh2_kex_method_strlen(LIBSSH2_COMMON_METHOD **method) +{ + size_t len = 0; + + if (!method || !*method) { + return 0; + } + + while (*method && (*method)->name) { + len += strlen((*method)->name) + 1; + method++; + } + + return len - 1; +} +/* }}} */ + +/* {{{ libssh2_kex_method_list + * Generate formatted preference list in buf + */ +static size_t libssh2_kex_method_list(unsigned char *buf, size_t list_strlen, LIBSSH2_COMMON_METHOD **method) +{ + libssh2_htonu32(buf, list_strlen); + buf += 4; + + if (!method || !*method) { + return 4; + } + + while (*method && (*method)->name) { + int mlen = strlen((*method)->name); + memcpy(buf, (*method)->name, mlen); + buf += mlen; + *(buf++) = ','; + method++; + } + + return list_strlen + 4; +} +/* }}} */ + +#define LIBSSH2_METHOD_PREFS_LEN(prefvar, defaultvar) ((prefvar) ? strlen(prefvar) : libssh2_kex_method_strlen((LIBSSH2_COMMON_METHOD**)(defaultvar))) +#define LIBSSH2_METHOD_PREFS_STR(buf, prefvarlen, prefvar, defaultvar) \ + if (prefvar) { \ + libssh2_htonu32((buf), (prefvarlen)); \ + buf += 4; \ + memcpy((buf), (prefvar), (prefvarlen)); \ + buf += (prefvarlen); \ + } else { \ + buf += libssh2_kex_method_list((buf), (prefvarlen), (LIBSSH2_COMMON_METHOD**)(defaultvar)); \ + } + +/* {{{ libssh2_kexinit + * Send SSH_MSG_KEXINIT packet + */ +static int libssh2_kexinit(LIBSSH2_SESSION *session) +{ + size_t data_len = 62; /* packet_type(1) + cookie(16) + first_packet_follows(1) + reserved(4) + length longs(40) */ + size_t kex_len, hostkey_len = 0; + size_t crypt_cs_len, crypt_sc_len; + size_t comp_cs_len, comp_sc_len; + size_t mac_cs_len, mac_sc_len; + size_t lang_cs_len, lang_sc_len; + unsigned char *data, *s; + + kex_len = LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); + hostkey_len = LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, libssh2_hostkey_methods()); + crypt_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, libssh2_crypt_methods()); + crypt_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, libssh2_crypt_methods()); + mac_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, libssh2_mac_methods()); + mac_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, libssh2_mac_methods()); + comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, libssh2_comp_methods()); + comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, libssh2_comp_methods()); + lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); + lang_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); + + data_len += kex_len + hostkey_len + \ + crypt_cs_len + crypt_sc_len + \ + comp_cs_len + comp_sc_len + \ + mac_cs_len + mac_sc_len + \ + lang_cs_len + lang_sc_len; + + s = data = (unsigned char *) LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory", 0); + return -1; + } + + *(s++) = SSH_MSG_KEXINIT; + + RAND_bytes(s, 16); + s += 16; + + /* Ennumerating through these lists twice is probably (certainly?) inefficient from a CPU standpoint, but it saves multiple malloc/realloc calls */ + LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, libssh2_kex_methods); + LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, libssh2_hostkey_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); + LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, NULL); + + /* No optimistic KEX packet follows */ + /* Deal with optimistic packets + * session->flags |= KEXINIT_OPTIMISTIC + * session->flags |= KEXINIT_METHODSMATCH + */ + *(s++) = 0; + + /* Reserved == 0 */ + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + +#ifdef LIBSSH2_DEBUG_KEX +{ + /* Funnily enough, they'll all "appear" to be '\0' terminated */ + char *p = data + 21; /* type(1) + cookie(16) + len(4) */ + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4; +} +#endif /* LIBSSH2_DEBUG_KEX */ + if (libssh2_packet_write(session, data, data_len)) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0); + return -1; + } + + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + } + + session->local.kexinit = data; + session->local.kexinit_len = data_len; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_instr + * Kex specific variant of strstr() + * Needle must be preceed by BOL or ',', and followed by ',' or EOL + */ +static unsigned char *libssh2_kex_agree_instr(unsigned char *haystack, unsigned long haystack_len, + unsigned char *needle, unsigned long needle_len) +{ + unsigned char *s; + + /* Haystack too short to bother trying */ + if (haystack_len < needle_len) { + return NULL; + } + + /* Needle at start of haystack */ + if ((strncmp((char *) haystack, (char *) needle, needle_len) == 0) && + (needle_len == haystack_len || haystack[needle_len] == ',')) { + return haystack; + } + + s = haystack; + /* Search until we run out of comas or we run out of haystack, + whichever comes first */ + while ((s = (unsigned char *) strchr((char *) s, ',')) && ((haystack_len - (s - haystack)) > needle_len)) { + s++; + /* Needle at X position */ + if ((strncmp((char *) s, (char *) needle, needle_len) == 0) && + (((s - haystack) + needle_len) == haystack_len || s[needle_len] == ',')) { + return s; + } + } + + return NULL; +} +/* }}} */ + +/* {{{ libssh2_get_method_by_name + */ +static LIBSSH2_COMMON_METHOD *libssh2_get_method_by_name(char *name, int name_len, LIBSSH2_COMMON_METHOD **methodlist) +{ + while (*methodlist) { + if ((strlen((char *) (*methodlist)->name) == (unsigned int) name_len) && + (strncmp((*methodlist)->name, name, name_len) == 0)) { + return *methodlist; + } + methodlist++; + } + return NULL; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_hostkey + * Agree on a Hostkey which works with this kex + */ +static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex_flags, unsigned char *hostkey, unsigned long hostkey_len) +{ + LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods(); + unsigned char *s; + + if (session->hostkey_prefs) { + s = (unsigned char *) session->hostkey_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + if (libssh2_kex_agree_instr(hostkey, hostkey_len, s, method_len)) { + LIBSSH2_HOSTKEY_METHOD *method = (LIBSSH2_HOSTKEY_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)hostkeyp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + /* So far so good, but does it suit our purposes? (Encrypting vs Signing) */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) || + (method->encrypt)) { + /* Either this hostkey can do encryption or this kex just doesn't require it */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) == 0) || + (method->sig_verify)) { + /* Either this hostkey can do signing or this kex just doesn't require it */ + session->hostkey = method; + return 0; + } + } + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (hostkeyp && (*hostkeyp)->name) { + s = libssh2_kex_agree_instr(hostkey, hostkey_len, (unsigned char *)(*hostkeyp)->name, strlen((*hostkeyp)->name)); + if (s) { + /* So far so good, but does it suit our purposes? (Encrypting vs Signing) */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) || + ((*hostkeyp)->encrypt)) { + /* Either this hostkey can do encryption or this kex just doesn't require it */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) == 0) || + ((*hostkeyp)->sig_verify)) { + /* Either this hostkey can do signing or this kex just doesn't require it */ + session->hostkey = *hostkeyp; + return 0; + } + } + } + hostkeyp++; + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_kex_hostkey + * Agree on a Key Exchange method and a hostkey encoding type + */ +static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char *kex, unsigned long kex_len, + unsigned char *hostkey, unsigned long hostkey_len) +{ + LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods; + unsigned char *s; + + if (session->kex_prefs) { + s = (unsigned char *) session->kex_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); + if (libssh2_kex_agree_instr(kex, kex_len, s, method_len)) { + LIBSSH2_KEX_METHOD *method = (LIBSSH2_KEX_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)kexp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + /* We've agreed on a key exchange method, + * Can we agree on a hostkey that works with this kex? + */ + if (libssh2_kex_agree_hostkey(session, method->flags, hostkey, hostkey_len) == 0) { + session->kex = method; + return 0; + } + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*kexp && (*kexp)->name) { + s = libssh2_kex_agree_instr(kex, kex_len, (unsigned char *)(*kexp)->name, strlen((*kexp)->name)); + if (s) { + /* We've agreed on a key exchange method, + * Can we agree on a hostkey that works with this kex? + */ + if (libssh2_kex_agree_hostkey(session, (*kexp)->flags, hostkey, hostkey_len) == 0) { + session->kex = *kexp; + return 0; + } + } + kexp++; + } + return -1; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_crypt + * Agree on a cipher algo + */ +static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *crypt, unsigned long crypt_len) +{ + LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods(); + unsigned char *s; + + if (endpoint->crypt_prefs) { + s = (unsigned char *) endpoint->crypt_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); + + if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) { + LIBSSH2_CRYPT_METHOD *method = (LIBSSH2_CRYPT_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)cryptp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->crypt = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*cryptp && (*cryptp)->name) { + s = libssh2_kex_agree_instr(crypt, crypt_len, (unsigned char *) (*cryptp)->name, strlen((*cryptp)->name)); + if (s) { + endpoint->crypt = *cryptp; + return 0; + } + cryptp++; + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_mac + * Agree on a message authentication hash + */ +static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *mac, unsigned long mac_len) +{ + LIBSSH2_MAC_METHOD **macp = libssh2_mac_methods(); + unsigned char *s; + + if (endpoint->mac_prefs) { + s = (unsigned char *)endpoint->mac_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *)strchr((char*)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); + + if (libssh2_kex_agree_instr(mac, mac_len, s, method_len)) { + LIBSSH2_MAC_METHOD *method = (LIBSSH2_MAC_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)macp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->mac = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*macp && (*macp)->name) { + s = libssh2_kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name, strlen((*macp)->name)); + if (s) { + endpoint->mac = *macp; + return 0; + } + macp++; + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_kex_agree_comp + * Agree on a compression scheme + */ +static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_data *endpoint, unsigned char *comp, unsigned long comp_len) +{ + LIBSSH2_COMP_METHOD **compp = libssh2_comp_methods(); + unsigned char *s; + + if (endpoint->comp_prefs) { + s = (unsigned char *)endpoint->comp_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *)s, ','); + int method_len = (p ? (p - s) : strlen((char *)s)); + + if (libssh2_kex_agree_instr(comp, comp_len, s, method_len)) { + LIBSSH2_COMP_METHOD *method = (LIBSSH2_COMP_METHOD*)libssh2_get_method_by_name((char *)s, method_len, (LIBSSH2_COMMON_METHOD**)compp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->comp = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*compp && (*compp)->name) { + s = libssh2_kex_agree_instr(comp, comp_len, (unsigned char *)(*compp)->name, strlen((*compp)->name)); + if (s) { + endpoint->comp = *compp; + return 0; + } + compp++; + } + + return -1; +} +/* }}} */ + +/* TODO: When in server mode we need to turn this logic on its head + * The Client gets to make the final call on "agreed methods" + */ + +/* {{{ libssh2_kex_agree_methods + * Decide which specific method to use of the methods offered by each party + */ +static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *data, unsigned data_len) +{ + unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc, *mac_cs, *mac_sc, *lang_cs, *lang_sc; + size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len, comp_sc_len, mac_cs_len, mac_sc_len, lang_cs_len, lang_sc_len; + unsigned char *s = data; + + /* Skip packet_type, we know it already */ + s++; + + /* Skip cookie, don't worry, it's preserved in the kexinit field */ + s += 16; + + /* Locate each string */ + kex_len = libssh2_ntohu32(s); kex = s + 4; s += 4 + kex_len; + hostkey_len = libssh2_ntohu32(s); hostkey = s + 4; s += 4 + hostkey_len; + crypt_cs_len = libssh2_ntohu32(s); crypt_cs = s + 4; s += 4 + crypt_cs_len; + crypt_sc_len = libssh2_ntohu32(s); crypt_sc = s + 4; s += 4 + crypt_sc_len; + mac_cs_len = libssh2_ntohu32(s); mac_cs = s + 4; s += 4 + mac_cs_len; + mac_sc_len = libssh2_ntohu32(s); mac_sc = s + 4; s += 4 + mac_sc_len; + comp_cs_len = libssh2_ntohu32(s); comp_cs = s + 4; s += 4 + comp_cs_len; + comp_sc_len = libssh2_ntohu32(s); comp_sc = s + 4; s += 4 + comp_sc_len; + lang_cs_len = libssh2_ntohu32(s); lang_cs = s + 4; s += 4 + lang_cs_len; + lang_sc_len = libssh2_ntohu32(s); lang_sc = s + 4; s += 4 + lang_sc_len; + + if (libssh2_kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) { + return -1; + } + + if (libssh2_kex_agree_crypt(session, &session->local, crypt_cs, crypt_cs_len) || + libssh2_kex_agree_crypt(session, &session->remote, crypt_sc, crypt_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) || + libssh2_kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) || + libssh2_kex_agree_comp(session, &session->remote, comp_sc, comp_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len) || + libssh2_kex_agree_lang(session, &session->remote, lang_sc, lang_sc_len)) { + return -1; + } + +#ifdef LIBSSH2_DEBUG_KEX + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on KEX method: %s", session->kex->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on HOSTKEY method: %s", session->hostkey->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_CS method: %s", session->local.crypt->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_SC method: %s", session->remote.crypt->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_CS method: %s", session->local.mac->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_SC method: %s", session->remote.mac->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_CS method: %s", session->local.comp->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_SC method: %s", session->remote.comp->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_CS method:"); /* None yet */ + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_SC method:"); /* None yet */ +#endif + + /* Initialize compression layer */ + if (session->local.comp && session->local.comp->init && + session->local.comp->init(session, 1, &session->local.comp_abstract)) { + return -1; + } + + if (session->remote.comp && session->remote.comp->init && + session->remote.comp->init(session, 0, &session->remote.comp_abstract)) { + return -1; + } + + return 0; +} +/* }}} */ + +/* {{{ libssh2_kex_exchange + * Exchange keys + * Returns 0 on success, non-zero on failure + */ +int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->flags |= SERVER */ +{ + unsigned char *data; + unsigned long data_len; + + /* Prevent loop in packet_add() */ + session->state |= LIBSSH2_STATE_EXCHANGING_KEYS; + + if (reexchange) { + session->kex = NULL; + + if (session->hostkey && session->hostkey->dtor) { + session->hostkey->dtor(session, &session->server_hostkey_abstract); + } + session->hostkey = NULL; + } + + if (!session->kex || !session->hostkey) { + if (libssh2_packet_require(session, SSH_MSG_KEXINIT, &data, &data_len)) { + return -1; + } + + if (session->remote.kexinit) { + LIBSSH2_FREE(session, session->remote.kexinit); + } + session->remote.kexinit = data; + session->remote.kexinit_len = data_len; + + if (libssh2_kexinit(session)) { + return -1; + } + + if (libssh2_kex_agree_methods(session, data, data_len)) { + return -1; + } + } + + if (session->kex->exchange_keys(session)) { + libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, "Unrecoverable error exchanging keys", 0); + return -1; + } + + /* Done with kexinit buffers */ + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + session->local.kexinit = NULL; + } + if (session->remote.kexinit) { + LIBSSH2_FREE(session, session->remote.kexinit); + session->remote.kexinit = NULL; + } + + session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_session_method_pref + * Set preferred method + */ +LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs) +{ + char **prefvar, *s, *newprefs; + int prefs_len = strlen(prefs); + LIBSSH2_COMMON_METHOD **mlist; + + switch (method_type) { + case LIBSSH2_METHOD_KEX: + prefvar = &session->kex_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_kex_methods; + break; + case LIBSSH2_METHOD_HOSTKEY: + prefvar = &session->hostkey_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_hostkey_methods(); + break; + case LIBSSH2_METHOD_CRYPT_CS: + prefvar = &session->local.crypt_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_crypt_methods(); + break; + case LIBSSH2_METHOD_CRYPT_SC: + prefvar = &session->remote.crypt_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_crypt_methods(); + break; + case LIBSSH2_METHOD_MAC_CS: + prefvar = &session->local.mac_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_mac_methods(); + break; + case LIBSSH2_METHOD_MAC_SC: + prefvar = &session->remote.mac_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_mac_methods(); + break; + case LIBSSH2_METHOD_COMP_CS: + prefvar = &session->local.comp_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_comp_methods(); + break; + case LIBSSH2_METHOD_COMP_SC: + prefvar = &session->remote.comp_prefs; + mlist = (LIBSSH2_COMMON_METHOD**)libssh2_comp_methods(); + break; + case LIBSSH2_METHOD_LANG_CS: + prefvar = &session->local.lang_prefs; + mlist = NULL; + break; + case LIBSSH2_METHOD_LANG_SC: + prefvar = &session->remote.lang_prefs; + mlist = NULL; + break; + default: + libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid parameter specified for method_type", 0); + return -1; + } + + s = newprefs = (char *)LIBSSH2_ALLOC(session, prefs_len + 1); + if (!newprefs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Error allocated space for method preferences", 0); + return -1; + } + memcpy(s, prefs, prefs_len + 1); + + while (s && *s) { + char *p = strchr(s, ','); + int method_len = p ? (p - s) : strlen(s); + + if (!libssh2_get_method_by_name(s, method_len, mlist)) { + /* Strip out unsupported method */ + if (p) { + memcpy(s, p + 1, strlen(s) - method_len); + } else { + if (s > newprefs) { + *(--s) = '\0'; + } else { + *s = '\0'; + } + } + } + + s = p ? (p + 1) : NULL; + } + + if (strlen(newprefs) == 0) { + libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, "The requested method(s) are not currently supported", 0); + LIBSSH2_FREE(session, newprefs); + return -1; + } + + if (*prefvar) { + LIBSSH2_FREE(session, *prefvar); + } + *prefvar = newprefs; + + return 0; +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/libssh2/libssh2.h b/pandora_agents/win32/ssh/libssh2/libssh2.h new file mode 100644 index 0000000000..8a9bc75db0 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/libssh2.h @@ -0,0 +1,400 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef LIBSSH2_H +#define LIBSSH2_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Allow alternate API prefix from CFLAGS or calling app */ +#ifndef LIBSSH2_API +# ifdef LIBSSH2_WIN32 +# ifdef LIBSSH2_LIBRARY +# define LIBSSH2_API __declspec(dllexport) +# else +# define LIBSSH2_API __declspec(dllimport) +# endif /* LIBSSH2_LIBRARY */ +# else /* !LIBSSH2_WIN32 */ +# define LIBSSH2_API +# endif /* LIBSSH2_WIN32 */ +#endif /* LIBSSH2_API */ + +#if defined(LIBSSH2_DARWIN) || (defined(LIBSSH2_WIN32) && !defined(_MSC_VER)) +# include +#endif + +#if defined(LIBSSH2_WIN32) && _MSC_VER < 1300 +typedef unsigned __int64 libssh2_uint64_t; +typedef __int64 libssh2_int64_t; +#else +typedef unsigned long long libssh2_uint64_t; +typedef long long libssh2_int64_t; +#endif + +#define LIBSSH2_VERSION "0.13" +#define LIBSSH2_APINO 200507211326 + +/* Part of every banner, user specified or not */ +#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION + +/* We *could* add a comment here if we so chose */ +#define LIBSSH2_SSH_DEFAULT_BANNER LIBSSH2_SSH_BANNER +#define LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF LIBSSH2_SSH_DEFAULT_BANNER "\r\n" + +/* Default generate and safe prime sizes for diffie-hellman-group-exchange-sha1 */ +#define LIBSSH2_DH_GEX_MINGROUP 1024 +#define LIBSSH2_DH_GEX_OPTGROUP 1536 +#define LIBSSH2_DH_GEX_MAXGROUP 2048 + +/* Defaults for pty requests */ +#define LIBSSH2_TERM_WIDTH 80 +#define LIBSSH2_TERM_HEIGHT 24 +#define LIBSSH2_TERM_WIDTH_PX 0 +#define LIBSSH2_TERM_HEIGHT_PX 0 + +/* 1/4 second */ +#define LIBSSH2_SOCKET_POLL_UDELAY 250000 +/* 0.25 * 120 == 30 seconds */ +#define LIBSSH2_SOCKET_POLL_MAXLOOPS 120 + +/* Maximum size to allow a payload to compress to, plays it safe by falling short of spec limits */ +#define LIBSSH2_PACKET_MAXCOMP 32000 + +/* Maximum size to allow a payload to deccompress to, plays it safe by allowing more than spec requires */ +#define LIBSSH2_PACKET_MAXDECOMP 40000 + +/* Maximum size for an inbound compressed payload, plays it safe by overshooting spec limits */ +#define LIBSSH2_PACKET_MAXPAYLOAD 40000 + +/* Malloc callbacks */ +#define LIBSSH2_ALLOC_FUNC(name) void *name(size_t count, void **abstract) +#define LIBSSH2_REALLOC_FUNC(name) void *name(void *ptr, size_t count, void **abstract) +#define LIBSSH2_FREE_FUNC(name) void name(void *ptr, void **abstract) + +typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT +{ + char* text; + unsigned int length; + unsigned char echo; +} LIBSSH2_USERAUTH_KBDINT_PROMPT; + +typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE +{ + char* text; + unsigned int length; +} LIBSSH2_USERAUTH_KBDINT_RESPONSE; + +/* 'keyboard-interactive' authentication callback */ +#define LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC(name_) void name_(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract) + +/* Callbacks for special SSH packets */ +#define LIBSSH2_IGNORE_FUNC(name) void name(LIBSSH2_SESSION *session, const char *message, int message_len, void **abstract) +#define LIBSSH2_DEBUG_FUNC(name) void name(LIBSSH2_SESSION *session, int always_display, const char *message, int message_len, const char *language, int language_len,void **abstract) +#define LIBSSH2_DISCONNECT_FUNC(name) void name(LIBSSH2_SESSION *session, int reason, const char *message, int message_len, const char *language, int language_len, void **abstract) +#define LIBSSH2_PASSWD_CHANGEREQ_FUNC(name) void name(LIBSSH2_SESSION *session, char **newpw, int *newpw_len, void **abstract) +#define LIBSSH2_MACERROR_FUNC(name) int name(LIBSSH2_SESSION *session, const char *packet, int packet_len, void **abstract) +#define LIBSSH2_X11_OPEN_FUNC(name) void name(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, char *shost, int sport, void **abstract) + +#define LIBSSH2_CHANNEL_CLOSE_FUNC(name) void name(LIBSSH2_SESSION *session, void **session_abstract, LIBSSH2_CHANNEL *channel, void **channel_abstract) + +/* libssh2_session_callback_set() constants */ +#define LIBSSH2_CALLBACK_IGNORE 0 +#define LIBSSH2_CALLBACK_DEBUG 1 +#define LIBSSH2_CALLBACK_DISCONNECT 2 +#define LIBSSH2_CALLBACK_MACERROR 3 +#define LIBSSH2_CALLBACK_X11 4 + +/* libssh2_session_method_pref() constants */ +#define LIBSSH2_METHOD_KEX 0 +#define LIBSSH2_METHOD_HOSTKEY 1 +#define LIBSSH2_METHOD_CRYPT_CS 2 +#define LIBSSH2_METHOD_CRYPT_SC 3 +#define LIBSSH2_METHOD_MAC_CS 4 +#define LIBSSH2_METHOD_MAC_SC 5 +#define LIBSSH2_METHOD_COMP_CS 6 +#define LIBSSH2_METHOD_COMP_SC 7 +#define LIBSSH2_METHOD_LANG_CS 8 +#define LIBSSH2_METHOD_LANG_SC 9 + +/* session.flags bits */ +#define LIBSSH2_FLAG_SIGPIPE 0x00000001 + +typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; +typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL; +typedef struct _LIBSSH2_LISTENER LIBSSH2_LISTENER; + +typedef struct _LIBSSH2_POLLFD { + unsigned char type; /* LIBSSH2_POLLFD_* below */ + + union { + int socket; /* File descriptors -- examined with system select() call */ + LIBSSH2_CHANNEL *channel; /* Examined by checking internal state */ + LIBSSH2_LISTENER *listener; /* Read polls only -- are inbound connections waiting to be accepted? */ + } fd; + + unsigned long events; /* Requested Events */ + unsigned long revents; /* Returned Events */ +} LIBSSH2_POLLFD; + +/* Poll FD Descriptor Types */ +#define LIBSSH2_POLLFD_SOCKET 1 +#define LIBSSH2_POLLFD_CHANNEL 2 +#define LIBSSH2_POLLFD_LISTENER 3 + +/* Note: Win32 Doesn't actually have a poll() implementation, so some of these values are faked with select() data */ +/* Poll FD events/revents -- Match sys/poll.h where possible */ +#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */ +#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */ +#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */ +#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */ +/* revents only */ +#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */ +#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket */ +#define LIBSSH2_POLLFD_SESSION_CLOSED 0x0010 /* Session Disconnect */ +#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */ +#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */ +#define LIBSSH2_POLLFD_CHANNEL_CLOSED 0x0080 /* Channel Disconnect */ +#define LIBSSH2_POLLFD_LISTENER_CLOSED 0x0080 /* Listener Disconnect */ + +/* Hash Types */ +#define LIBSSH2_HOSTKEY_HASH_MD5 1 +#define LIBSSH2_HOSTKEY_HASH_SHA1 2 + +/* Disconnect Codes (defined by SSH protocol) */ +#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH_DISCONNECT_RESERVED 4 +#define SSH_DISCONNECT_MAC_ERROR 5 +#define SSH_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH_DISCONNECT_CONNECTION_LOST 10 +#define SSH_DISCONNECT_BY_APPLICATION 11 +#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15 + +/* Error Codes (defined by libssh2) */ +#define LIBSSH2_ERROR_SOCKET_NONE -1 +#define LIBSSH2_ERROR_BANNER_NONE -2 +#define LIBSSH2_ERROR_BANNER_SEND -3 +#define LIBSSH2_ERROR_INVALID_MAC -4 +#define LIBSSH2_ERROR_KEX_FAILURE -5 +#define LIBSSH2_ERROR_ALLOC -6 +#define LIBSSH2_ERROR_SOCKET_SEND -7 +#define LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE -8 +#define LIBSSH2_ERROR_TIMEOUT -9 +#define LIBSSH2_ERROR_HOSTKEY_INIT -10 +#define LIBSSH2_ERROR_HOSTKEY_SIGN -11 +#define LIBSSH2_ERROR_DECRYPT -12 +#define LIBSSH2_ERROR_SOCKET_DISCONNECT -13 +#define LIBSSH2_ERROR_PROTO -14 +#define LIBSSH2_ERROR_PASSWORD_EXPIRED -15 +#define LIBSSH2_ERROR_FILE -16 +#define LIBSSH2_ERROR_METHOD_NONE -17 +#define LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED -18 +#define LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED -19 +#define LIBSSH2_ERROR_CHANNEL_OUTOFORDER -20 +#define LIBSSH2_ERROR_CHANNEL_FAILURE -21 +#define LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED -22 +#define LIBSSH2_ERROR_CHANNEL_UNKNOWN -23 +#define LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED -24 +#define LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED -25 +#define LIBSSH2_ERROR_CHANNEL_CLOSED -26 +#define LIBSSH2_ERROR_CHANNEL_EOF_SENT -27 +#define LIBSSH2_ERROR_SCP_PROTOCOL -28 +#define LIBSSH2_ERROR_ZLIB -29 +#define LIBSSH2_ERROR_SOCKET_TIMEOUT -30 +#define LIBSSH2_ERROR_SFTP_PROTOCOL -31 +#define LIBSSH2_ERROR_REQUEST_DENIED -32 +#define LIBSSH2_ERROR_METHOD_NOT_SUPPORTED -33 +#define LIBSSH2_ERROR_INVAL -34 +#define LIBSSH2_ERROR_INVALID_POLL_TYPE -35 +#define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36 + +/* Session API */ +LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), LIBSSH2_FREE_FUNC((*my_free)), LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract); +#define libssh2_session_init() libssh2_session_init_ex(NULL, NULL, NULL, NULL) +LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session); + +LIBSSH2_API void *libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbtype, void *callback); +LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner); + +LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket); +LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang); +#define libssh2_session_disconnect(session, description) libssh2_session_disconnect_ex((session), SSH_DISCONNECT_BY_APPLICATION, (description), "") +LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session); + +LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type); + +LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs); +LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type); +LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf); + +LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int value); + +/* Userauth API */ +LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, int username_len); +LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session); +LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, int username_len, const char *password, int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))); +#define libssh2_userauth_password(session, username, password) libssh2_userauth_password_ex((session), (username), strlen(username), (password), strlen(password), NULL) + +LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + const char *publickey, const char *privatekey, + const char *passphrase); +#define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase) \ + libssh2_userauth_publickey_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase)) +LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + const char *publickey, const char *privatekey, + const char *passphrase, + const char *hostname, int hostname_len, + const char *local_username, int local_username_len); +#define libssh2_userauth_hostbased_fromfile(session, username, publickey, privatekey, passphrase, hostname) \ + libssh2_userauth_hostbased_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase), (hostname), strlen(hostname), (username), strlen(username)) + +/* + * response_callback is provided with filled by library prompts array, + * but client must allocate and fill individual responses. Responses + * array is already allocated. Responses data will be freed by libssh2 + * after callback return, but before subsequent callback invokation. + */ +LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION* session, const char *username, int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))); +#define libssh2_userauth_keyboard_interactive(session, username, response_callback) \ + libssh2_userauth_keyboard_interactive_ex((session), (username), strlen(username), (response_callback)) + +LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeout); + +/* Channel API */ +#define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536 +#define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384 +#define LIBSSH2_CHANNEL_MINADJUST 1024 + +/* Extended Data Handling */ +#define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0 +#define LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE 1 +#define LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE 2 + +#define SSH_EXTENDED_DATA_STDERR 1 + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, int channel_type_len, int window_size, int packet_size, const char *message, int message_len); +#define libssh2_channel_open_session(session) libssh2_channel_open_ex((session), "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0) + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, char *host, int port, char *shost, int sport); +#define libssh2_channel_direct_tcpip(session, host, port) libssh2_channel_direct_tcpip_ex((session), (host), (port), "127.0.0.1", 22) + +LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize); +#define libssh2_channel_forward_listen(session, port) libssh2_channel_forward_listen_ex((session), NULL, (port), NULL, 16) + +LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener); + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener); + +LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len); +#define libssh2_channel_setenv(channel, varname, value) libssh2_channel_setenv_ex((channel), (varname), strlen(varname), (value), strlen(value)) + +LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *term, int term_len, char *modes, int modes_len, int width, int height, int width_px, int height_px); +#define libssh2_channel_request_pty(channel, term) libssh2_channel_request_pty_ex((channel), (term), strlen(term), NULL, 0, LIBSSH2_TERM_WIDTH, LIBSSH2_TERM_HEIGHT, LIBSSH2_TERM_WIDTH_PX, LIBSSH2_TERM_HEIGHT_PX) + +LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number); +#define libssh2_channel_x11_req(channel, screen_number) libssh2_channel_x11_req_ex((channel), 0, NULL, NULL, (screen_number)) + +LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, int request_len, const char *message, int message_len); +#define libssh2_channel_shell(channel) libssh2_channel_process_startup((channel), "shell", sizeof("shell") - 1, NULL, 0) +#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command)) +#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem)) + +LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen); +#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen)) +#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) + +LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended); + +LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial); +#define libssh2_channel_window_read(channel) libssh2_channel_window_read_ex((channel), NULL, NULL) + +LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force); + +LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen); +#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen)) +#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) + +LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial); +#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL) + +LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking); +LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); +/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1 + * Future uses should use libssh2_channel_handle_extended_data() directly + * if LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read (FIFO) from the standard data channel + */ +/* DEPRECATED */ +#define libssh2_channel_ignore_extended_data(channel, ignore) libssh2_channel_handle_extended_data((channel), (ignore) ? LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE : LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL ) + +#define LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA -1 +#define LIBSSH2_CHANNEL_FLUSH_ALL -2 +LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid); +#define libssh2_channel_flush(channel) libssh2_channel_flush_ex((channel), 0) +#define libssh2_channel_flush_stderr(channel) libssh2_channel_flush_ex((channel), SSH_EXTENDED_DATA_STDERR) +LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel); + +LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel); + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb); +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime); +#define libssh2_scp_send(session, path, mode, size) libssh2_scp_send_ex((session), (path), (mode), (size), 0, 0) + +LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **dest, int *dest_len, char *src, int src_len); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSSH2_H */ diff --git a/pandora_agents/win32/ssh/libssh2/libssh2_config.h b/pandora_agents/win32/ssh/libssh2/libssh2_config.h new file mode 100644 index 0000000000..c79b4baa90 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/libssh2_config.h @@ -0,0 +1,38 @@ +#ifndef WIN32 +#define WIN32 +#endif +#include +#include +#include + +/* same as WSABUF */ +struct iovec { + u_long iov_len; + char *iov_base; +}; + +#define inline __inline + +static inline int writev(int sock, struct iovec *iov, int nvecs) +{ + DWORD ret; + if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) { + return ret; + } + return -1; +} + +/* not really usleep, but safe for the way we use it in this lib */ +static inline int usleep(int udelay) +{ + Sleep(udelay / 1000); + return 0; +} + +#define snprintf _snprintf + +/* Compile in zlib support */ +#define LIBSSH2_HAVE_ZLIB 1 + +/* Enable newer diffie-hellman-group-exchange-sha1 syntax */ +#define LIBSSH2_DH_GEX_NEW 1 diff --git a/pandora_agents/win32/ssh/libssh2/libssh2_priv.h b/pandora_agents/win32/ssh/libssh2/libssh2_priv.h new file mode 100644 index 0000000000..f68510c7e0 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/libssh2_priv.h @@ -0,0 +1,479 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef LIBSSH2_PRIV_H +#define LIBSSH2_PRIV_H 1 + +#define LIBSSH2_LIBRARY +#include "libssh2_config.h" +#include "libssh2.h" + +#ifndef WIN32 +#include +#endif +#include +#ifndef OPENSSL_NO_SHA +#include +#endif +#ifndef OPENSSL_NO_MD5 +#include +#endif + +#ifdef __hpux +# define inline +#endif + +#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract) +#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract)) +#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract) + +#define LIBSSH2_IGNORE(session, data, datalen) session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract) +#define LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len) \ + session->ssh_msg_disconnect((session), (always_display), (message), (message_len), (language), (language_len), &(session)->abstract) +#define LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len) \ + session->ssh_msg_disconnect((session), (reason), (message), (message_len), (language), (language_len), &(session)->abstract) + +#define LIBSSH2_MACERROR(session, data, datalen) session->macerror((session), (data), (datalen), &(session)->abstract) +#define LIBSSH2_X11_OPEN(channel, shost, sport) channel->session->x11(((channel)->session), (channel), (shost), (sport), (&(channel)->session->abstract)) + +#define LIBSSH2_CHANNEL_CLOSE(session, channel) channel->close_cb((session), &(session)->abstract, (channel), &(channel)->abstract) + +typedef struct _LIBSSH2_KEX_METHOD LIBSSH2_KEX_METHOD; +typedef struct _LIBSSH2_HOSTKEY_METHOD LIBSSH2_HOSTKEY_METHOD; +typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD; +typedef struct _LIBSSH2_CRYPT_METHOD LIBSSH2_CRYPT_METHOD; +typedef struct _LIBSSH2_COMP_METHOD LIBSSH2_COMP_METHOD; + +typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET; +typedef struct _LIBSSH2_PACKET_BRIGADE LIBSSH2_PACKET_BRIGADE; +typedef struct _LIBSSH2_CHANNEL_BRIGADE LIBSSH2_CHANNEL_BRIGADE; + +struct _LIBSSH2_PACKET { + unsigned char type; + + /* Unencrypted Payload (no type byte, no padding, just the facts ma'am) */ + unsigned char *data; + unsigned long data_len; + + /* Where to start reading data from, + * used for channel data that's been partially consumed */ + unsigned long data_head; + + /* Can the message be confirmed? */ + int mac; + + LIBSSH2_PACKET_BRIGADE *brigade; + + LIBSSH2_PACKET *next, *prev; +}; + +struct _LIBSSH2_PACKET_BRIGADE { + LIBSSH2_PACKET *head, *tail; +}; + +typedef struct _libssh2_channel_data { + /* Identifier */ + unsigned long id; + + /* Limits and restrictions */ + unsigned long window_size_initial, window_size, packet_size; + + /* Set to 1 when CHANNEL_CLOSE / CHANNEL_EOF sent/received */ + char close, eof, extended_data_ignore_mode; +} libssh2_channel_data; + +struct _LIBSSH2_CHANNEL { + unsigned char *channel_type; + unsigned channel_type_len; + + int blocking; + + /* channel's program exit status */ + int exit_status; + + libssh2_channel_data local, remote; + unsigned long adjust_queue; /* Amount of bytes to be refunded to receive window (but not yet sent) */ + + LIBSSH2_SESSION *session; + + LIBSSH2_CHANNEL *next, *prev; + + void *abstract; + LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb)); +}; + +struct _LIBSSH2_CHANNEL_BRIGADE { + LIBSSH2_CHANNEL *head, *tail; +}; + +struct _LIBSSH2_LISTENER { + LIBSSH2_SESSION *session; + + char *host; + int port; + + LIBSSH2_CHANNEL *queue; + int queue_size; + int queue_maxsize; + + LIBSSH2_LISTENER *prev, *next; +}; + +typedef struct _libssh2_endpoint_data { + unsigned char *banner; + + unsigned char *kexinit; + unsigned long kexinit_len; + + LIBSSH2_CRYPT_METHOD *crypt; + void *crypt_abstract; + + LIBSSH2_MAC_METHOD *mac; + unsigned long seqno; + void *mac_abstract; + + LIBSSH2_COMP_METHOD *comp; + void *comp_abstract; + + /* Method Preferences -- NULL yields "load order" */ + char *crypt_prefs; + char *mac_prefs; + char *comp_prefs; + char *lang_prefs; +} libssh2_endpoint_data; + +struct _LIBSSH2_SESSION { + /* Memory management callbacks */ + void *abstract; + LIBSSH2_ALLOC_FUNC((*alloc)); + LIBSSH2_REALLOC_FUNC((*realloc)); + LIBSSH2_FREE_FUNC((*free)); + + /* Other callbacks */ + LIBSSH2_IGNORE_FUNC((*ssh_msg_ignore)); + LIBSSH2_DEBUG_FUNC((*ssh_msg_debug)); + LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect)); + LIBSSH2_MACERROR_FUNC((*macerror)); + LIBSSH2_X11_OPEN_FUNC((*x11)); + + /* Method preferences -- NULL yields "load order" */ + char *kex_prefs; + char *hostkey_prefs; + + int state; + int flags; + + /* Agreed Key Exchange Method */ + LIBSSH2_KEX_METHOD *kex; + + unsigned char *session_id; + unsigned long session_id_len; + + /* Server's public key */ + LIBSSH2_HOSTKEY_METHOD *hostkey; + void *server_hostkey_abstract; + + /* Either set with libssh2_session_hostkey() (for server mode) + * Or read from server in (eg) KEXDH_INIT (for client mode) + */ + unsigned char *server_hostkey; + unsigned long server_hostkey_len; +#ifndef OPENSSL_NO_MD5 + unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH]; +#endif /* ! OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH]; +#endif + + /* (remote as source of data -- packet_read ) */ + libssh2_endpoint_data remote; + + /* (local as source of data -- packet_write ) */ + libssh2_endpoint_data local; + + /* Inbound Data buffer -- Sometimes the packet that comes in isn't the packet we're ready for */ + LIBSSH2_PACKET_BRIGADE packets; + + /* Active connection channels */ + LIBSSH2_CHANNEL_BRIGADE channels; + unsigned long next_channel; + + LIBSSH2_LISTENER *listeners; + + /* Actual I/O socket */ + int socket_fd; + int socket_block; + int socket_state; + + /* Error tracking */ + char *err_msg; + unsigned long err_msglen; + int err_should_free; + int err_code; +}; + +/* session.state bits */ +#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001 +#define LIBSSH2_STATE_NEWKEYS 0x00000002 +#define LIBSSH2_STATE_AUTHENTICATED 0x00000004 + +/* session.flag helpers */ +#ifdef MSG_NOSIGNAL +#define LIBSSH2_SOCKET_SEND_FLAGS(session) (((session)->flags & LIBSSH2_FLAG_SIGPIPE) ? 0 : MSG_NOSIGNAL) +#define LIBSSH2_SOCKET_RECV_FLAGS(session) (((session)->flags & LIBSSH2_FLAG_SIGPIPE) ? 0 : MSG_NOSIGNAL) +#else +/* If MSG_NOSIGNAL isn't defined we're SOL on blocking SIGPIPE */ +#define LIBSSH2_SOCKET_SEND_FLAGS(session) 0 +#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0 +#endif + +/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional methods via .so/.dll */ + +struct _LIBSSH2_KEX_METHOD { + char *name; + + /* Key exchange, populates session->* and returns 0 on success, non-0 on error */ + int (*exchange_keys)(LIBSSH2_SESSION *session); + + long flags; +}; + +struct _LIBSSH2_HOSTKEY_METHOD { + char *name; + unsigned long hash_len; + + int (*init)(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract); + int (*initPEM)(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract); + int (*sig_verify)(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len, const unsigned char *m, unsigned long m_len, void **abstract); + int (*sign)(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, const unsigned char *data, unsigned long data_len, void **abstract); + int (*signv)(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, unsigned long veccount, const struct iovec datavec[], void **abstract); + int (*encrypt)(LIBSSH2_SESSION *session, unsigned char **dst, unsigned long *dst_len, const unsigned char *src, unsigned long src_len, void **abstract); + int (*dtor)(LIBSSH2_SESSION *session, void **abstract); +}; + +/* When FLAG_EVP is set, crypt contains a pointer to an EVP_CIPHER generator and init and dtor are ignored + * Yes, I know it's a hack. + */ + +#define LIBSSH2_CRYPT_METHOD_FLAG_EVP 0x0001 + +struct _LIBSSH2_CRYPT_METHOD { + char *name; + + int blocksize; + + /* iv and key sizes (-1 for variable length) */ + int iv_len; + int secret_len; + + long flags; + + int (*init)(LIBSSH2_SESSION *session, unsigned char *iv, int *free_iv, unsigned char *secret, int *free_secret, int encrypt, void **abstract); + int (*crypt)(LIBSSH2_SESSION *session, unsigned char *block, void **abstract); + int (*dtor)(LIBSSH2_SESSION *session, void **abstract); +}; + +struct _LIBSSH2_COMP_METHOD { + char *name; + + int (*init)(LIBSSH2_SESSION *session, int compress, void **abstract); + int (*comp)(LIBSSH2_SESSION *session, int compress, unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest, + const unsigned char *src, unsigned long src_len, void **abstract); + int (*dtor)(LIBSSH2_SESSION *session, int compress, void **abstract); +}; + +struct _LIBSSH2_MAC_METHOD { + char *name; + + /* The length of a given MAC packet */ + int mac_len; + + /* integrity key length */ + int key_len; + + /* Message Authentication Code Hashing algo */ + int (*init)(LIBSSH2_SESSION *session, unsigned char *key, int *free_key, void **abstract); + int (*hash)(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, const unsigned char *packet, unsigned long packet_len, const unsigned char *addtl, unsigned long addtl_len, void **abstract); + int (*dtor)(LIBSSH2_SESSION *session, void **abstract); +}; + +#if defined(LIBSSH2_DEBUG_TRANSPORT) || defined(LIBSSH2_DEBUG_KEX) || defined(LIBSSH2_DEBUG_USERAUTH) || defined(LIBSSH2_DEBUG_CONNECTION) || defined(LIBSSH2_DEBUG_SCP) || defined(LIBSSH2_DEBUG_SFTP) || defined(LIBSSH2_DEBUG_ERRORS) +#define LIBSSH2_DEBUG_ENABLED + +/* Internal debugging contexts -- Used with --enable-debug-* */ +#define LIBSSH2_DBG_TRANS 1 +#define LIBSSH2_DBG_KEX 2 +#define LIBSSH2_DBG_AUTH 3 +#define LIBSSH2_DBG_CONN 4 +#define LIBSSH2_DBG_SCP 5 +#define LIBSSH2_DBG_SFTP 6 +#define LIBSSH2_DBG_ERROR 7 +#define LIBSSH2_DBG_PUBLICKEY 8 + +void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...); + +#endif /* LIBSSH2_DEBUG_ENABLED */ + +#ifdef LIBSSH2_DEBUG_ERRORS +#define libssh2_error(session, errcode, errmsg, should_free) \ +{ \ + if (session->err_msg && session->err_should_free) { \ + LIBSSH2_FREE(session, session->err_msg); \ + } \ + session->err_msg = errmsg; \ + session->err_msglen = strlen(errmsg); \ + session->err_should_free = should_free; \ + session->err_code = errcode; \ + _libssh2_debug(session, LIBSSH2_DBG_ERROR, "%d - %s", session->err_code, session->err_msg); \ +} + +#else /* ! LIBSSH2_DEBUG_ERRORS */ + +#define libssh2_error(session, errcode, errmsg, should_free) \ +{ \ + if (session->err_msg && session->err_should_free) { \ + LIBSSH2_FREE(session, session->err_msg); \ + } \ + session->err_msg = errmsg; \ + session->err_msglen = strlen(errmsg); \ + session->err_should_free = should_free; \ + session->err_code = errcode; \ +} + +#endif /* LIBSSH2_DEBUG_ENABLED */ + + +#define LIBSSH2_SOCKET_UNKNOWN 1 +#define LIBSSH2_SOCKET_CONNECTED 0 +#define LIBSSH2_SOCKET_DISCONNECTED -1 + +/* Initial packet state, prior to MAC check */ +#define LIBSSH2_MAC_UNCONFIRMED 1 +/* When MAC type is "none" (proto initiation phase) all packets are deemed "confirmed" */ +#define LIBSSH2_MAC_CONFIRMED 0 +/* Something very bad is going on */ +#define LIBSSH2_MAC_INVALID -1 + +/* SSH Packet Types -- Defined by internet draft */ +/* Transport Layer */ +#define SSH_MSG_DISCONNECT 1 +#define SSH_MSG_IGNORE 2 +#define SSH_MSG_UNIMPLEMENTED 3 +#define SSH_MSG_DEBUG 4 +#define SSH_MSG_SERVICE_REQUEST 5 +#define SSH_MSG_SERVICE_ACCEPT 6 + +#define SSH_MSG_KEXINIT 20 +#define SSH_MSG_NEWKEYS 21 + +/* diffie-hellman-group1-sha1 */ +#define SSH_MSG_KEXDH_INIT 30 +#define SSH_MSG_KEXDH_REPLY 31 + +/* diffie-hellman-group-exchange-sha1 */ +#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH_MSG_KEX_DH_GEX_REQUEST 34 +#define SSH_MSG_KEX_DH_GEX_GROUP 31 +#define SSH_MSG_KEX_DH_GEX_INIT 32 +#define SSH_MSG_KEX_DH_GEX_REPLY 33 + +/* User Authentication */ +#define SSH_MSG_USERAUTH_REQUEST 50 +#define SSH_MSG_USERAUTH_FAILURE 51 +#define SSH_MSG_USERAUTH_SUCCESS 52 +#define SSH_MSG_USERAUTH_BANNER 53 + +/* "public key" method */ +#define SSH_MSG_USERAUTH_PK_OK 60 +/* "password" method */ +#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +/* "keyboard-interactive" method */ +#define SSH_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH_MSG_USERAUTH_INFO_RESPONSE 61 + +/* Channels */ +#define SSH_MSG_GLOBAL_REQUEST 80 +#define SSH_MSG_REQUEST_SUCCESS 81 +#define SSH_MSG_REQUEST_FAILURE 82 + +#define SSH_MSG_CHANNEL_OPEN 90 +#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH_MSG_CHANNEL_DATA 94 +#define SSH_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH_MSG_CHANNEL_EOF 96 +#define SSH_MSG_CHANNEL_CLOSE 97 +#define SSH_MSG_CHANNEL_REQUEST 98 +#define SSH_MSG_CHANNEL_SUCCESS 99 +#define SSH_MSG_CHANNEL_FAILURE 100 + +void libssh2_session_shutdown(LIBSSH2_SESSION *session); + +unsigned long libssh2_ntohu32(const unsigned char *buf); +libssh2_uint64_t libssh2_ntohu64(const unsigned char *buf); +void libssh2_htonu32(unsigned char *buf, unsigned long val); +void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val); + +int libssh2_packet_read(LIBSSH2_SESSION *session, int block); +int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket); +#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \ + libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket)) +int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket); +#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \ + libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket)) +int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len); +#define libssh2_packet_require(session, packet_type, data, data_len) \ + libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0) +int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len); +#define libssh2_packet_requirev(session, packet_types, data, data_len) \ + libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0) +int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len); +int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange); +unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session); +LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id); + +/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */ +LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void); +LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void); +LIBSSH2_COMP_METHOD **libssh2_comp_methods(void); +LIBSSH2_MAC_METHOD **libssh2_mac_methods(void); + +/* Language API doesn't exist yet. Just act like we've agreed on a language */ +#define libssh2_kex_agree_lang(session, endpoint, str, str_len) 0 + +#endif /* LIBSSH2_H */ diff --git a/pandora_agents/win32/ssh/libssh2/libssh2_publickey.h b/pandora_agents/win32/ssh/libssh2/libssh2_publickey.h new file mode 100644 index 0000000000..44b46e95f1 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/libssh2_publickey.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* Note: This include file is only needed for using the + * publickey SUBSYSTEM which is not the same as publickey + * authentication. For authentication you only need libssh2.h + * + * For more information on the publickey subsystem, + * refer to IETF draft: secsh-publickey + */ + +#ifndef LIBSSH2_PUBLICKEY_H +#define LIBSSH2_PUBLICKEY_H 1 + +typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY; + +typedef struct _libssh2_publickey_attribute { + char *name; + unsigned long name_len; + char *value; + unsigned long value_len; + char mandatory; +} libssh2_publickey_attribute; + +typedef struct _libssh2_publickey_list { + unsigned char *packet; /* For freeing */ + + unsigned char *name; + unsigned long name_len; + unsigned char *blob; + unsigned long blob_len; + unsigned long num_attrs; + libssh2_publickey_attribute *attrs; /* free me */ +} libssh2_publickey_list; + +/* Generally use the first macro here, but if both name and value are string literals, you can use _fast() to take advantage of preprocessing */ +#define libssh2_publickey_attribute(name, value, mandatory) { (name), strlen(name), (value), strlen(value), (mandatory) }, +#define libssh2_publickey_attribute_fast(name, value, mandatory) { (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) }, + +#ifdef __cplusplus +extern "C" { +#endif + +/* Publickey Subsystem */ +LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session); + +LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len, char overwrite, + unsigned long num_attrs, libssh2_publickey_attribute attrs[]); +#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, num_attrs, attrs) \ + libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), (overwrite), (num_attrs), (attrs)) + +LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len); +#define libssh2_publickey_remove(pkey, name, blob, blob_len) \ + libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len)) + +LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list); +LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list); + +LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef: LIBSSH2_PUBLICKEY_H */ diff --git a/pandora_agents/win32/ssh/libssh2/libssh2_sftp.h b/pandora_agents/win32/ssh/libssh2/libssh2_sftp.h new file mode 100644 index 0000000000..e950fbfd6e --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/libssh2_sftp.h @@ -0,0 +1,198 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef LIBSSH2_SFTP_H +#define LIBSSH2_SFTP_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Note: Version 6 was documented at the time of writing + * However it was marked as "DO NOT IMPLEMENT" due to pending changes + * + * Let's start with Version 3 (The version found in OpenSSH) and go from there + */ +#define LIBSSH2_SFTP_VERSION 3 +#define LIBSSH2_SFTP_PACKET_MAXLEN 40000 + +typedef struct _LIBSSH2_SFTP LIBSSH2_SFTP; +typedef struct _LIBSSH2_SFTP_HANDLE LIBSSH2_SFTP_HANDLE; +typedef struct _LIBSSH2_SFTP_ATTRIBUTES LIBSSH2_SFTP_ATTRIBUTES; + +/* Flags for open_ex() */ +#define LIBSSH2_SFTP_OPENFILE 0 +#define LIBSSH2_SFTP_OPENDIR 1 + +/* Flags for rename_ex() */ +#define LIBSSH2_SFTP_RENAME_OVERWRITE 0x00000001 +#define LIBSSH2_SFTP_RENAME_ATOMIC 0x00000002 +#define LIBSSH2_SFTP_RENAME_NATIVE 0x00000004 + +/* Flags for stat_ex() */ +#define LIBSSH2_SFTP_STAT 0 +#define LIBSSH2_SFTP_LSTAT 1 +#define LIBSSH2_SFTP_SETSTAT 2 + +/* Flags for symlink_ex() */ +#define LIBSSH2_SFTP_SYMLINK 0 +#define LIBSSH2_SFTP_READLINK 1 +#define LIBSSH2_SFTP_REALPATH 2 + +/* SFTP attribute flag bits */ +#define LIBSSH2_SFTP_ATTR_SIZE 0x00000001 +#define LIBSSH2_SFTP_ATTR_UIDGID 0x00000002 +#define LIBSSH2_SFTP_ATTR_PERMISSIONS 0x00000004 +#define LIBSSH2_SFTP_ATTR_ACMODTIME 0x00000008 +#define LIBSSH2_SFTP_ATTR_EXTENDED 0x80000000 + +struct _LIBSSH2_SFTP_ATTRIBUTES { + /* If flags & ATTR_* bit is set, then the value in this struct will be meaningful + * Otherwise it should be ignored + */ + unsigned long flags; + + libssh2_uint64_t filesize; + unsigned long uid, gid; + unsigned long permissions; + unsigned long atime, mtime; +}; + +/* SFTP filetypes */ +#define LIBSSH2_SFTP_TYPE_REGULAR 1 +#define LIBSSH2_SFTP_TYPE_DIRECTORY 2 +#define LIBSSH2_SFTP_TYPE_SYMLINK 3 +#define LIBSSH2_SFTP_TYPE_SPECIAL 4 +#define LIBSSH2_SFTP_TYPE_UNKNOWN 5 +#define LIBSSH2_SFTP_TYPE_SOCKET 6 +#define LIBSSH2_SFTP_TYPE_CHAR_DEVICE 7 +#define LIBSSH2_SFTP_TYPE_BLOCK_DEVICE 8 +#define LIBSSH2_SFTP_TYPE_FIFO 9 + +/* SFTP File Transfer Flags -- (e.g. flags parameter to sftp_open()) + * Danger will robinson... APPEND doesn't have any effect on OpenSSH servers */ +#define LIBSSH2_FXF_READ 0x00000001 +#define LIBSSH2_FXF_WRITE 0x00000002 +#define LIBSSH2_FXF_APPEND 0x00000004 +#define LIBSSH2_FXF_CREAT 0x00000008 +#define LIBSSH2_FXF_TRUNC 0x00000010 +#define LIBSSH2_FXF_EXCL 0x00000020 + +/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */ +#define LIBSSH2_FX_OK 0 +#define LIBSSH2_FX_EOF 1 +#define LIBSSH2_FX_NO_SUCH_FILE 2 +#define LIBSSH2_FX_PERMISSION_DENIED 3 +#define LIBSSH2_FX_FAILURE 4 +#define LIBSSH2_FX_BAD_MESSAGE 5 +#define LIBSSH2_FX_NO_CONNECTION 6 +#define LIBSSH2_FX_CONNECTION_LOST 7 +#define LIBSSH2_FX_OP_UNSUPPORTED 8 +#define LIBSSH2_FX_INVALID_HANDLE 9 +#define LIBSSH2_FX_NO_SUCH_PATH 10 +#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11 +#define LIBSSH2_FX_WRITE_PROTECT 12 +#define LIBSSH2_FX_NO_MEDIA 13 +#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14 +#define LIBSSH2_FX_QUOTA_EXCEEDED 15 +#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 +#define LIBSSH2_FX_LOCK_CONFlICT 17 +#define LIBSSH2_FX_DIR_NOT_EMPTY 18 +#define LIBSSH2_FX_NOT_A_DIRECTORY 19 +#define LIBSSH2_FX_INVALID_FILENAME 20 +#define LIBSSH2_FX_LINK_LOOP 21 + +/* SFTP API */ +LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session); +LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp); +LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp); + +/* File / Directory Ops */ +LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *filename, int filename_len, unsigned long flags, long mode, int open_type); +#define libssh2_sftp_open(sftp, filename, flags, mode) libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), (mode), LIBSSH2_SFTP_OPENFILE) +#define libssh2_sftp_opendir(sftp, path) libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR) + +LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen); +LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs); +LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count); + +LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle); +#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle) +#define libssh2_sftp_closedir(handle) libssh2_sftp_close_handle(handle) + +LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset); +#define libssh2_sftp_rewind(handle) libssh2_sftp_seek((handle), 0) + +LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle); + +LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat); +#define libssh2_sftp_fstat(handle, attrs) libssh2_sftp_fstat_ex((handle), (attrs), 0) +#define libssh2_sftp_fsetstat(handle, attrs) libssh2_sftp_fstat_ex((handle), (attrs), 1) + + + +/* Miscellaneous Ops */ +LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filename, int srouce_filename_len, + char *dest_filename, int dest_filename_len, + long flags); +#define libssh2_sftp_rename(sftp, sourcefile, destfile) libssh2_sftp_rename_ex((sftp), (sourcefile), strlen(sourcefile), (destfile), strlen(destfile), \ + LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE) + +LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, int filename_len); +#define libssh2_sftp_unlink(sftp, filename) libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename)) + +LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_len, long mode); +#define libssh2_sftp_mkdir(sftp, path, mode) libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode)) + +LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_len); +#define libssh2_sftp_rmdir(sftp, path) libssh2_sftp_rmdir_ex((sftp), (path), strlen(path)) + +LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES *attrs); +#define libssh2_sftp_stat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_STAT, (attrs)) +#define libssh2_sftp_lstat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_LSTAT, (attrs)) +#define libssh2_sftp_setstat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_SETSTAT, (attrs)) + +LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, int path_len, char *target, int target_len, int link_type); +#define libssh2_sftp_symlink(sftp, orig, linkpath) libssh2_sftp_symlink_ex((sftp), (orig), strlen(orig), (linkpath), strlen(linkpath), LIBSSH2_SFTP_SYMLINK) +#define libssh2_sftp_readlink(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_READLINK) +#define libssh2_sftp_realpath(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_REALPATH) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSSH2_SFTP_H */ diff --git a/pandora_agents/win32/ssh/libssh2/mac.c b/pandora_agents/win32/ssh/libssh2/mac.c new file mode 100644 index 0000000000..9cba7c08eb --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/mac.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include + +#ifdef LIBSSH2_MAC_NONE +/* {{{ libssh2_mac_none_MAC + * Minimalist MAC: No MAC + */ +static int libssh2_mac_none_MAC(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + return 0; +} +/* }}} */ + + +static LIBSSH2_MAC_METHOD libssh2_mac_method_none = { + "none", + 0, + 0, + NULL, + libssh2_mac_none_MAC, + NULL +}; +#endif /* LIBSSH2_MAC_NONE */ + +/* {{{ libssh2_mac_method_common_init + * Initialize simple mac methods + */ +static int libssh2_mac_method_common_init(LIBSSH2_SESSION *session, unsigned char *key, int *free_key, void **abstract) +{ + *abstract = key; + *free_key = 0; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_mac_method_common_dtor + * Cleanup simple mac methods + */ +static int libssh2_mac_method_common_dtor(LIBSSH2_SESSION *session, void **abstract) +{ + if (*abstract) { + LIBSSH2_FREE(session, *abstract); + } + *abstract = NULL; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_mac_method_hmac_sha1_hash + * Calculate hash using full sha1 value + */ +static int libssh2_mac_method_hmac_sha1_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + HMAC_CTX ctx; + unsigned char seqno_buf[4]; + + libssh2_htonu32(seqno_buf, seqno); + + HMAC_Init(&ctx, *abstract, 20, EVP_sha1()); + HMAC_Update(&ctx, seqno_buf, 4); + HMAC_Update(&ctx, packet, packet_len); + if (addtl && addtl_len) { + HMAC_Update(&ctx, addtl, addtl_len); + } + HMAC_Final(&ctx, buf, NULL); + HMAC_cleanup(&ctx); + + return 0; +} +/* }}} */ + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1 = { + "hmac-sha1", + 20, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_sha1_hash, + libssh2_mac_method_common_dtor, +}; + +/* {{{ libssh2_mac_method_hmac_sha1_96_hash + * Calculate hash using first 96 bits of sha1 value + */ +static int libssh2_mac_method_hmac_sha1_96_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + char temp[SHA_DIGEST_LENGTH]; + + libssh2_mac_method_hmac_sha1_hash(session, (unsigned char *) temp, seqno, packet, packet_len, addtl, addtl_len, abstract); + memcpy(buf, temp, 96 / 8); + + return 0; +} +/* }}} */ + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1_96 = { + "hmac-sha1-96", + 12, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_sha1_96_hash, + libssh2_mac_method_common_dtor, +}; + +/* {{{ libssh2_mac_method_hmac_md5_hash + * Calculate hash using full md5 value + */ +static int libssh2_mac_method_hmac_md5_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + HMAC_CTX ctx; + unsigned char seqno_buf[4]; + + libssh2_htonu32(seqno_buf, seqno); + + HMAC_Init(&ctx, *abstract, 16, EVP_md5()); + HMAC_Update(&ctx, seqno_buf, 4); + HMAC_Update(&ctx, packet, packet_len); + if (addtl && addtl_len) { + HMAC_Update(&ctx, addtl, addtl_len); + } + HMAC_Final(&ctx, buf, NULL); + HMAC_cleanup(&ctx); + + return 0; +} +/* }}} */ + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5 = { + "hmac-md5", + 16, + 16, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_md5_hash, + libssh2_mac_method_common_dtor, +}; + +/* {{{ libssh2_mac_method_hmac_md5_96_hash + * Calculate hash using first 96 bits of md5 value + */ +static int libssh2_mac_method_hmac_md5_96_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + char temp[MD5_DIGEST_LENGTH]; + + libssh2_mac_method_hmac_md5_hash(session, (unsigned char *)temp, seqno, packet, packet_len, addtl, addtl_len, abstract); + memcpy(buf, temp, 96 / 8); + + return 0; +} +/* }}} */ + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5_96 = { + "hmac-md5-96", + 12, + 16, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_md5_96_hash, + libssh2_mac_method_common_dtor, +}; + +#ifndef OPENSSL_NO_RIPEMD +/* {{{ libssh2_mac_method_hmac_ripemd160_hash + * Calculate hash using ripemd160 value + */ +static int libssh2_mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, + const unsigned char *packet, unsigned long packet_len, + const unsigned char *addtl, unsigned long addtl_len, void **abstract) +{ + HMAC_CTX ctx; + unsigned char seqno_buf[4]; + + libssh2_htonu32(seqno_buf, seqno); + + HMAC_Init(&ctx, *abstract, 20, EVP_ripemd160()); + HMAC_Update(&ctx, seqno_buf, 4); + HMAC_Update(&ctx, packet, packet_len); + if (addtl && addtl_len) { + HMAC_Update(&ctx, addtl, addtl_len); + } + HMAC_Final(&ctx, buf, NULL); + HMAC_cleanup(&ctx); + + return 0; +} +/* }}} */ + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160 = { + "hmac-ripemd160", + 20, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_ripemd160_hash, + libssh2_mac_method_common_dtor, +}; + +static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160_openssh_com = { + "hmac-ripemd160@openssh.com", + 20, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_ripemd160_hash, + libssh2_mac_method_common_dtor, +}; +#endif /* ! OPENSSL_NO_RIPEMD */ + +static LIBSSH2_MAC_METHOD *_libssh2_mac_methods[] = { + &libssh2_mac_method_hmac_sha1, + &libssh2_mac_method_hmac_sha1_96, + &libssh2_mac_method_hmac_md5, + &libssh2_mac_method_hmac_md5_96, +#ifndef OPENSSL_NO_RIPEMD + &libssh2_mac_method_hmac_ripemd160, + &libssh2_mac_method_hmac_ripemd160_openssh_com, +#endif /* ! OPENSSL_NO_RIPEMD */ +#ifdef LIBSSH2_MAC_NONE + &libssh2_mac_method_none, +#endif /* LIBSSH2_MAC_NONE */ + NULL +}; + +LIBSSH2_MAC_METHOD **libssh2_mac_methods(void) { + return _libssh2_mac_methods; +} + diff --git a/pandora_agents/win32/ssh/libssh2/misc.c b/pandora_agents/win32/ssh/libssh2/misc.c new file mode 100644 index 0000000000..5d6257ce57 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/misc.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" + +/* {{{ libssh2_ntohu32 + */ +unsigned long libssh2_ntohu32(const unsigned char *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} +/* }}} */ + +/* {{{ libssh2_ntohu64 + * Note: Some 32-bit platforms have issues with bitops on long longs + * Work around this by doing expensive (but safer) arithmetic ops with optimization defying parentheses + */ +libssh2_uint64_t libssh2_ntohu64(const unsigned char *buf) +{ + unsigned long msl, lsl; + + msl = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + lsl = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; + + return ((msl * 65536) * 65536) + lsl; +} +/* }}} */ + +/* {{{ libssh2_htonu32 + */ +void libssh2_htonu32(unsigned char *buf, unsigned long value) +{ + buf[0] = (value >> 24) & 0xFF; + buf[1] = (value >> 16) & 0xFF; + buf[2] = (value >> 8) & 0xFF; + buf[3] = value & 0xFF; +} +/* }}} */ + +/* {{{ libssh2_htonu64 + */ +void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t value) +{ + unsigned long msl = (value / 65536) / 65536; + + buf[0] = (msl >> 24) & 0xFF; + buf[1] = (msl >> 16) & 0xFF; + buf[2] = (msl >> 8) & 0xFF; + buf[3] = msl & 0xFF; + + buf[4] = (value >> 24) & 0xFF; + buf[5] = (value >> 16) & 0xFF; + buf[6] = (value >> 8) & 0xFF; + buf[7] = value & 0xFF; +} +/* }}} */ + +/* Base64 Conversion */ + +/* {{{ */ +static const char libssh2_base64_table[] = + { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' + }; + +static const char libssh2_base64_pad = '='; + +static const short libssh2_base64_reverse_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; +/* }}} */ + + +/* {{{ libssh2_base64_decode + * Decode a base64 chunk and store it into a newly alloc'd buffer + */ +LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **data, int *datalen, + char *src, int src_len) +{ + unsigned char *s, *d; + short v; + int i = 0, len = 0; + + d = (unsigned char *) LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1); + *data = (char *) d; + if (!d) { + return -1; + } + + for(s = (unsigned char *)src; ((char*)s) < (src + src_len); s++) { + if ((v = libssh2_base64_reverse_table[*s]) < 0) continue; + switch (i % 4) { + case 0: + d[len] = v << 2; + break; + case 1: + d[len++] |= v >> 4; + d[len] = v << 4; + break; + case 2: + d[len++] |= v >> 2; + d[len] = v << 6; + break; + case 3: + d[len++] |= v; + break; + } + i++; + } + if ((i % 4) == 1) { + /* Invalid -- We have a byte which belongs exclusively to a partial octet */ + LIBSSH2_FREE(session, *data); + return -1; + } + + *datalen = len; + return 0; +} +/* }}} */ + +#ifdef LIBSSH2_DEBUG_ENABLED +/* {{{ _libssh2_debug + * Internal debug logging facility + * Just writes to stderr until a good reason comes up to support anything else + */ +void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...) +{ + char buffer[1536]; + int len; + va_list vargs; + FILE *file; /* TODO: Change to fstream */ + + char *contexts[9] = { "Unknown", + "Transport", + "Key Exhange", + "Userauth", + "Connection", + "scp", + "SFTP Subsystem", + "Failure Event", + "Publickey Subsystem", + }; + + if (context < 1 || context > 8) { + context = 0; + } + + len = snprintf(buffer, 1535, "[libssh2] %s: ", contexts[context]); + + va_start(vargs, format); + len += vsnprintf(buffer + len, 1535 - len, format, vargs); + buffer[len] = '\0'; + va_end(vargs); + + fprintf (stderr, "%s\n", buffer); +} +/* }}} */ +#endif diff --git a/pandora_agents/win32/ssh/libssh2/packet.c b/pandora_agents/win32/ssh/libssh2/packet.c new file mode 100644 index 0000000000..2da351c745 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/packet.c @@ -0,0 +1,1182 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_POLL +# include +#else +# ifdef HAVE_SELECT +# ifdef HAVE_SYS_SELECT_H +# include +# else +# include +# include +# endif +# endif +#endif + +/* {{{ libssh2_packet_queue_listener + * Queue a connection request for a listener + */ +inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) +{ + /* Look for a matching listener */ + unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; + unsigned long packet_len = 17 + (sizeof("Forward not requested") - 1); + unsigned char *p, packet[17 + (sizeof("Forward not requested") - 1)]; + /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + LIBSSH2_LISTENER *l = session->listeners; + char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ + unsigned long sender_channel, initial_window_size, packet_size; + unsigned char *host, *shost; + unsigned long port, sport, host_len, shost_len; + + sender_channel = libssh2_ntohu32(s); s += 4; + + initial_window_size = libssh2_ntohu32(s); s += 4; + packet_size = libssh2_ntohu32(s); s += 4; + + host_len = libssh2_ntohu32(s); s += 4; + host = s; s += host_len; + port = libssh2_ntohu32(s); s += 4; + + shost_len = libssh2_ntohu32(s); s += 4; + shost = s; s += shost_len; + sport = libssh2_ntohu32(s); s += 4; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port); +#endif + while (l) { + if ((l->port == (int)port) && + (strlen(l->host) == host_len) && + (memcmp(l->host, host, host_len) == 0)) { + /* This is our listener */ + LIBSSH2_CHANNEL *channel, *last_queued = l->queue; + + if (l->queue_maxsize && + (l->queue_maxsize <= l->queue_size)) { + /* Queue is full */ + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring"); +#endif + break; + } + + channel = (LIBSSH2_CHANNEL *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + break; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->session = session; + channel->channel_type_len = sizeof("forwarded-tcpip") - 1; + channel->channel_type = (unsigned char *) LIBSSH2_ALLOC(session, channel->channel_type_len + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + break; + } + memcpy(channel->channel_type, "forwarded-tcpip", channel->channel_type_len + 1); + + channel->remote.id = sender_channel; + channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = initial_window_size; + channel->local.window_size = initial_window_size; + channel->local.packet_size = packet_size; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, channel->remote.window_size, + channel->local.packet_size, channel->remote.packet_size); +#endif + + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); p += 4; + libssh2_htonu32(p, channel->local.id); p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; + libssh2_htonu32(p, channel->remote.packet_size); p += 4; + + if (libssh2_packet_write(session, packet, 17)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); + return -1; + } + + /* Link the channel into the end of the queue list */ + + if (!last_queued) { + l->queue = channel; + return 0; + } + + while (last_queued->next) last_queued = last_queued->next; + + last_queued->next = channel; + channel->prev = last_queued; + + l->queue_size++; + + return 0; + } + + l = l->next; + } + + /* We're not listening to you */ + { + + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, sender_channel); p += 4; + libssh2_htonu32(p, failure_code); p += 4; + libssh2_htonu32(p, sizeof("Forward not requested") - 1); p += 4; + memcpy(s, "Forward not requested", sizeof("Forward not requested") - 1); p += sizeof("Forward not requested") - 1; + libssh2_htonu32(p, 0); + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); + return -1; + } + return 0; + } +} +/* }}} */ + +/* {{{ libssh2_packet_x11_open + * Accept a forwarded X11 connection + */ +inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data, unsigned long datalen) +{ + int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ + unsigned char *s = data + (sizeof("x11") - 1) + 5; + unsigned long packet_len = 17 + (sizeof("X11 Forward Unavailable") - 1); + unsigned char *p, packet[17 + (sizeof("X11 Forward Unavailable") - 1)]; + /* packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + LIBSSH2_CHANNEL *channel; + unsigned long sender_channel, initial_window_size, packet_size; + unsigned char *shost; + unsigned long sport, shost_len; + + sender_channel = libssh2_ntohu32(s); s += 4; + initial_window_size = libssh2_ntohu32(s); s += 4; + packet_size = libssh2_ntohu32(s); s += 4; + shost_len = libssh2_ntohu32(s); s += 4; + shost = s; s += shost_len; + sport = libssh2_ntohu32(s); s += 4; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel); +#endif + if (session->x11) { + channel = (LIBSSH2_CHANNEL *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->session = session; + channel->channel_type_len = sizeof("x11") - 1; + channel->channel_type = (unsigned char *) LIBSSH2_ALLOC(session, channel->channel_type_len + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a channel for new connection", 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memcpy(channel->channel_type, "x11", channel->channel_type_len + 1); + + channel->remote.id = sender_channel; + channel->remote.window_size_initial = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = initial_window_size; + channel->local.window_size = initial_window_size; + channel->local.packet_size = packet_size; + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, channel->remote.window_size, + channel->local.packet_size, channel->remote.packet_size); +#endif + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); p += 4; + libssh2_htonu32(p, channel->local.id); p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); p += 4; + libssh2_htonu32(p, channel->remote.packet_size); p += 4; + + if (libssh2_packet_write(session, packet, 17)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0); + return -1; + } + + /* Link the channel into the session */ + if (session->channels.tail) { + session->channels.tail->next = channel; + channel->prev = session->channels.tail; + } else { + session->channels.head = channel; + channel->prev = NULL; + } + channel->next = NULL; + session->channels.tail = channel; + + /* Pass control to the callback, they may turn right around and free the channel, or actually use it */ + LIBSSH2_X11_OPEN(channel, (char *) shost, sport); + + return 0; + } else { + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + } + + x11_exit: + p = packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, sender_channel); p += 4; + libssh2_htonu32(p, failure_code); p += 4; + libssh2_htonu32(p, sizeof("X11 Forward Unavailable") - 1); p += 4; + memcpy(s, "X11 Forward Unavailable", sizeof("X11 Forward Unavailable") - 1); p += sizeof("X11 Forward Unavailable") - 1; + libssh2_htonu32(p, 0); + + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send open failure", 0); + return -1; + } + return 0; +} +/* }}} */ + +/* {{{ libssh2_packet_new + * Create a new packet and attach it to the brigade + */ +static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate) +{ + LIBSSH2_PACKET *packet; + unsigned long data_head = 0; + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen); +#endif + if (macstate == LIBSSH2_MAC_INVALID) { + if (session->macerror) { + if (LIBSSH2_MACERROR(session, (char *)data, datalen) == 0) { + /* Calling app has given the OK, Process it anyway */ + macstate = LIBSSH2_MAC_CONFIRMED; + } else { + libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); + } + return -1; + } + } else { + libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0); + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0); + } + return -1; + } + } + + /* A couple exceptions to the packet adding rule: */ + switch (data[0]) { + case SSH_MSG_DISCONNECT: + { + char *message, *language; + int reason, message_len, language_len; + + reason = libssh2_ntohu32(data + 1); + message_len = libssh2_ntohu32(data + 5); + message = (char *)data + 9; /* packet_type(1) + reason(4) + message_len(4) */ + language_len = libssh2_ntohu32(data + 9 + message_len); + /* This is where we hack on the data a little, + * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) + * Shift the language tag back a byte (In all likelihood it's zero length anyway + * Store a NULL in the last byte of the packet to terminate the language string + * With the lengths passed this isn't *REALLY* necessary, but it's "kind" + */ + message[message_len] = '\0'; + language = (char *)data + 9 + message_len + 3; + if (language_len) { + memcpy(language, language + 1, language_len); + } + language[language_len] = '\0'; + + if (session->ssh_msg_disconnect) { + LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len); + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language); +#endif + LIBSSH2_FREE(session, data); + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + return -1; + } + break; + case SSH_MSG_IGNORE: + /* As with disconnect, back it up one and add a trailing NULL */ + memcpy(data + 4, data + 5, datalen - 5); + data[datalen] = '\0'; + if (session->ssh_msg_ignore) { + LIBSSH2_IGNORE(session, (char *)data + 4, datalen - 5); + } + LIBSSH2_FREE(session, data); + return 0; + break; + case SSH_MSG_DEBUG: + { + int always_display = data[0]; + char *message, *language; + int message_len, language_len; + + message_len = libssh2_ntohu32(data + 2); + message = (char *)data + 6; /* packet_type(1) + display(1) + message_len(4) */ + language_len = libssh2_ntohu32(data + 6 + message_len); + /* This is where we hack on the data a little, + * Use the MSB of language_len to to a terminating NULL (In all liklihood it is already) + * Shift the language tag back a byte (In all likelihood it's zero length anyway + * Store a NULL in the last byte of the packet to terminate the language string + * With the lengths passed this isn't *REALLY* necessary, but it's "kind" + */ + message[message_len] = '\0'; + language = (char *)data + 6 + message_len + 3; + if (language_len) { + memcpy(language, language + 1, language_len); + } + language[language_len] = '\0'; + + if (session->ssh_msg_debug) { + LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len); + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + /* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message); +#endif + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_EXTENDED_DATA: + data_head += 4; /* streamid(4) */ + case SSH_MSG_CHANNEL_DATA: + data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */ + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0); + LIBSSH2_FREE(session, data); + return 0; + } +#ifdef LIBSSH2_DEBUG_CONNECTION +{ + unsigned long stream_id = 0; + + if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) { + stream_id = libssh2_ntohu32(data + 5); + } + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id); +} +#endif + if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { + /* Pretend we didn't receive this */ + LIBSSH2_FREE(session, data); + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13)); +#endif + /* Adjust the window based on the block we just freed */ + libssh2_channel_receive_window_adjust(channel, datalen - 13, 0); + + return 0; + } + + /* REMEMBER! remote means remote as source of data, NOT remote window! */ + if (channel->remote.packet_size < (datalen - data_head)) { + /* Spec says we MAY ignore bytes sent beyond packet_size */ + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0); + datalen = channel->remote.packet_size + data_head; + } + if (channel->remote.window_size <= 0) { + /* Spec says we MAY ignore bytes sent beyond window_size */ + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0); + LIBSSH2_FREE(session, data); + return 0; + } + /* Reset EOF status */ + channel->remote.eof = 0; + + if ((datalen - data_head) > channel->remote.window_size) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0); + datalen = channel->remote.window_size + data_head; + } else { + /* Now that we've received it, shrink our window */ + channel->remote.window_size -= datalen - data_head; + } + } + break; + case SSH_MSG_CHANNEL_EOF: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + + if (!channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + return 0; + } + +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + channel->remote.eof = 1; + + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_REQUEST: + { + if (libssh2_ntohu32(data+5) == sizeof("exit-status") - 1 + && !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) { + + /* we've got "exit-status" packet. Set the session value */ + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1)); + + if (channel) { + channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status")); +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id); +#endif + } + + LIBSSH2_FREE(session, data); + return 0; + } + } + break; + case SSH_MSG_CHANNEL_CLOSE: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + + if (!channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + return 0; + } +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id); +#endif + + channel->remote.close = 1; + /* TODO: Add a callback for this */ + + LIBSSH2_FREE(session, data); + return 0; + } + break; + case SSH_MSG_CHANNEL_OPEN: + if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && + ((sizeof("forwarded-tcpip")-1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "forwarded-tcpip", sizeof("forwarded-tcpip") - 1) == 0)) { + int retval = libssh2_packet_queue_listener(session, data, datalen); + + LIBSSH2_FREE(session, data); + return retval; + } + if ((datalen >= (sizeof("x11") + 4)) && + ((sizeof("x11")-1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { + int retval = libssh2_packet_x11_open(session, data, datalen); + + LIBSSH2_FREE(session, data); + return retval; + } + break; + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + { + LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1)); + unsigned long bytestoadd = libssh2_ntohu32(data + 5); + + if (channel && bytestoadd) { + channel->local.window_size += bytestoadd; + } +#ifdef LIBSSH2_DEBUG_CONNECTION + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size); +#endif + + LIBSSH2_FREE(session, data); + return 0; + } + break; + } + + packet = (LIBSSH2_PACKET *)LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + memset(packet, 0, sizeof(LIBSSH2_PACKET)); + + packet->data = data; + packet->data_len = datalen; + packet->data_head = data_head; + packet->mac = macstate; + packet->brigade = &session->packets; + packet->next = NULL; + + if (session->packets.tail) { + packet->prev = session->packets.tail; + packet->prev->next = packet; + session->packets.tail = packet; + } else { + session->packets.head = packet; + session->packets.tail = packet; + packet->prev = NULL; + } + + if (data[0] == SSH_MSG_KEXINIT && !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) { + /* Remote wants new keys + * Well, it's already in the brigade, + * let's just call back into ourselves + */ +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); +#endif + libssh2_kex_exchange(session, 1); + /* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */ + } + + return 0; +} +/* }}} */ + +/* {{{ libssh2_blocking_read + * Force a blocking read, regardless of socket settings + */ +static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count) +{ + size_t bytes_read = 0; +#if !defined(HAVE_POLL) && !defined(HAVE_SELECT) + int polls = 0; +#endif + +#ifndef WIN32 + fcntl(session->socket_fd, F_SETFL, 0); +#else + { + u_long block = FALSE; + ioctlsocket(session->socket_fd, FIONBIO, &block); + } +#endif + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count); +#endif + + while (bytes_read < count) { + int ret; + + ret = recv(session->socket_fd, (char *)buf + bytes_read, count - bytes_read, LIBSSH2_SOCKET_RECV_FLAGS(session)); + if (ret < 0) { +#ifdef WIN32 + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: errno = EAGAIN; break; + case WSAENOTCONN: + case WSAENOTSOCK: + case WSAECONNABORTED: errno = EBADF; break; + case WSAEINTR: errno = EINTR; break; + } +#endif + if (errno == EAGAIN) { +#ifdef HAVE_POLL + struct pollfd read_socket; + + read_socket.fd = session->socket_fd; + read_socket.events = POLLIN; + + if (poll(&read_socket, 1, 30000) <= 0) { + return -1; + } +#elif defined(HAVE_SELECT) + fd_set read_socket; + struct timeval timeout; + + FD_ZERO(&read_socket); + FD_SET(session->socket_fd, &read_socket); + + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) { + return -1; + } +#else + if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) { + return -1; + } + usleep(LIBSSH2_SOCKET_POLL_UDELAY); +#endif /* POLL/SELECT/SLEEP */ + continue; + } + if (errno == EINTR) { + continue; + } + if ((errno == EBADF) || (errno == EIO)) { + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + } + return -1; + } + if (ret == 0) continue; + + bytes_read += ret; + } + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read); +#endif + + return bytes_read; +} +/* }}} */ + +/* {{{ libssh2_packet_read + * Collect a packet into the input brigade + * block only controls whether or not to wait for a packet to start, + * Once a packet starts, libssh2 will block until it is complete + * Returns packet type added to input brigade (0 if nothing added), or -1 on failure + */ +int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block) +{ + int packet_type = -1; + + if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { + return 0; + } + +#ifndef WIN32 + fcntl(session->socket_fd, F_SETFL, O_NONBLOCK); +#else + { + u_long non_block = TRUE; + ioctlsocket(session->socket_fd, FIONBIO, &non_block); + } +#endif + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not"); +#endif + if (session->state & LIBSSH2_STATE_NEWKEYS) { + /* Temporary Buffer + * The largest blocksize (currently) is 32, the largest MAC (currently) is 20 + */ + unsigned char block[2 * 32], *payload, *s, tmp[6]; + long read_len; + unsigned long blocksize = session->remote.crypt->blocksize; + unsigned long packet_len, payload_len; + int padding_len; + int macstate; + int free_payload = 1; + /* Safely ignored in CUSTOM cipher mode */ + EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->remote.crypt_abstract; + + /* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this + * For now, all blocksize sizes are 8+ + */ + if (should_block) { + read_len = libssh2_blocking_read(session, block, blocksize); + } else { + read_len = recv(session->socket_fd, (char *)block, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); + if (read_len <= 0) { + return 0; + } + read_len += libssh2_blocking_read(session, block + read_len, blocksize - read_len); + } + if (read_len < (int)blocksize) { + return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; + } + + if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + EVP_Cipher(ctx, block + blocksize, block, blocksize); + memcpy(block, block + blocksize, blocksize); + } else { + if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); + return -1; + } + } + + packet_len = libssh2_ntohu32(block); + padding_len = block[4]; +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len); +#endif + memcpy(tmp, block, 5); /* Use this for MAC later */ + + payload_len = packet_len - 1; /* padding_len(1) */ + /* Sanity Check */ + if ((payload_len > LIBSSH2_PACKET_MAXPAYLOAD) || + ((packet_len + 4) % blocksize)) { + /* If something goes horribly wrong during the decryption phase, just bailout and die gracefully */ + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Fatal protocol error, invalid payload size", 0); + return -1; + } + + s = payload = (unsigned char *)LIBSSH2_ALLOC(session, payload_len); + memcpy(s, block + 5, blocksize - 5); + s += blocksize - 5; + + while ((s - payload) < (int)payload_len) { + read_len = libssh2_blocking_read(session, block, blocksize); + if (read_len < (int)blocksize) { + LIBSSH2_FREE(session, payload); + return -1; + } + if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + EVP_Cipher(ctx, block + blocksize, block, blocksize); + memcpy(s, block + blocksize, blocksize); + } else { + if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0); + LIBSSH2_FREE(session, payload); + return -1; + } + memcpy(s, block, blocksize); + } + + s += blocksize; + } + + read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len); + if (read_len < session->remote.mac->mac_len) { + LIBSSH2_FREE(session, payload); + return -1; + } + + /* Calculate MAC hash */ + session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract); + + macstate = (strncmp((char *)block, (char *)block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID; + + session->remote.seqno++; + + /* Ignore padding */ + payload_len -= padding_len; + + if (session->remote.comp && + strcmp(session->remote.comp->name, "none")) { + /* Decompress */ + unsigned char *data; + unsigned long data_len; + + if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) { + LIBSSH2_FREE(session, payload); + return -1; + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len); +#endif + if (free_payload) { + LIBSSH2_FREE(session, payload); + payload = data; + payload_len = data_len; + } else { + if (data == payload) { + /* It's not to be freed, because the compression layer reused payload, + * So let's do the same! + */ + payload_len = data_len; + } else { + /* No comp_method actually lets this happen, but let's prepare for the future */ + + LIBSSH2_FREE(session, payload); + + /* We need a freeable struct otherwise the brigade won't know what to do with it */ + payload = (unsigned char *)LIBSSH2_ALLOC(session, data_len); + if (!payload) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0); + return -1; + } + memcpy(payload, data, data_len); + payload_len = data_len; + } + } + } + + libssh2_packet_add(session, payload, payload_len, macstate); + + packet_type = payload[0]; + } else { /* No cipher active */ + unsigned char *payload; + unsigned char buf[24]; + unsigned long buf_len, payload_len; + unsigned long packet_length; + unsigned long padding_length; + + if (should_block) { + buf_len = libssh2_blocking_read(session, buf, 5); + } else { + buf_len = recv(session->socket_fd, (char *)buf, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); + if (buf_len <= 0) { + return 0; + } + buf_len += libssh2_blocking_read(session, buf, 5 - buf_len); + } + if (buf_len < 5) { + /* Something bad happened */ + return -1; + } + packet_length = libssh2_ntohu32(buf); + padding_length = buf[4]; +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length); +#endif + + payload_len = packet_length - padding_length - 1; /* padding_length(1) */ + payload = (unsigned char *)LIBSSH2_ALLOC(session, payload_len); + if (!payload) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0); + return -1; + } + + if (libssh2_blocking_read(session, payload, payload_len) < (int) payload_len) { + return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1; + } + while (padding_length) { + int l; + /* Flush padding */ + l = libssh2_blocking_read(session, buf, padding_length); + if (l > 0) + padding_length -= l; + else + break; + } + + /* MACs don't exist in non-encrypted mode */ + libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED); + session->remote.seqno++; + + packet_type = payload[0]; + } + return packet_type; +} +/* }}} */ + +/* {{{ libssh2_packet_ask + * Scan the brigade for a matching packet type, optionally poll the socket for a packet first + */ +int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) +{ + LIBSSH2_PACKET *packet = session->packets.head; + + if (poll_socket) { + if (libssh2_packet_read(session, 0) < 0) { + return -1; + } + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type); +#endif + while (packet) { + if (packet->data[0] == packet_type && + (packet->data_len >= (match_ofs + match_len)) && + (!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) { + *data = packet->data; + *data_len = packet->data_len; + + if (packet->prev) { + packet->prev->next = packet->next; + } else { + session->packets.head = packet->next; + } + + if (packet->next) { + packet->next->prev = packet->prev; + } else { + session->packets.tail = packet->prev; + } + + LIBSSH2_FREE(session, packet); + + return 0; + } + packet = packet->next; + } + return -1; +} +/* }}} */ + +/* {{{ libssh2_packet_askv + * Scan for any of a list of packet types in the brigade, optionally poll the socket for a packet first + */ +int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) +{ + int i, packet_types_len = strlen((char *)packet_types); + + for(i = 0; i < packet_types_len; i++) { + if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, data_len, match_ofs, match_buf, match_len, i ? 0 : poll_socket)) { + return 0; + } + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_packet_require + * Loops libssh2_packet_read() until the packet requested is available + * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout + */ +int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) +{ + if (libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { + /* A packet was available in the packet brigade */ + return 0; + } + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type); +#endif + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret = libssh2_packet_read(session, 1); + if (ret < 0) { + return -1; + } + if (ret == 0) continue; + + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; +} +/* }}} */ + +/* {{{ libssh2_packet_requirev + * Loops libssh2_packet_read() until one of a list of packet types requested is available + * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout + * packet_types is a null terminated list of packet_type numbers + */ +int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) +{ + if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { + /* One of the packets listed was available in the packet brigade */ + return 0; + } + + while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { + int ret = libssh2_packet_read(session, 1); + if (ret < 0) { + return -1; + } + if (ret == 0) { + continue; + } + + if (strchr((char *)packet_types, ret)) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; +} +/* }}} */ + +/* {{{ libssh2_packet_write + * Send a packet, encrypting it and adding a MAC code if necessary + * Returns 0 on success, non-zero on failure + */ +int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len) +{ + unsigned long packet_length = data_len + 1; + unsigned long block_size = (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt->blocksize : 8; + /* At this point packet_length doesn't include the packet_len field itself */ + unsigned long padding_length; + int free_data = 0; + unsigned char buf[246]; /* 6 byte header plus max padding size(240) */ + +#ifdef LIBSSH2_DEBUG_TRANSPORT +{ + /* Show a hint of what's being sent */ + char excerpt[32]; + int ex_len = 0, db_ofs = 0; + + for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]); + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt); +} +#endif + if ((session->state & LIBSSH2_STATE_NEWKEYS) && + strcmp(session->local.comp->name, "none")) { + + if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) { + return -1; + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len); +#endif + } + +#ifndef WIN32 + fcntl(session->socket_fd, F_SETFL, 0); +#else + { + u_long non_block = FALSE; + ioctlsocket(session->socket_fd, FIONBIO, &non_block); + } +#endif + + packet_length = data_len + 1; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */ + padding_length = block_size - ((packet_length + 4) % block_size); + if (padding_length < 4) { + padding_length += block_size; + } + /* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */ + + packet_length += padding_length; + libssh2_htonu32(buf, packet_length); + buf[4] = padding_length; +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length); +#endif + + if (session->state & LIBSSH2_STATE_NEWKEYS) { + /* Encryption is in effect */ + unsigned char *encbuf, *s; + int ret; + + /* Safely ignored in CUSTOM cipher mode */ + EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->local.crypt_abstract; + + /* include packet_length(4) itself and room for the hash at the end */ + encbuf = (unsigned char *)LIBSSH2_ALLOC(session, 4 + packet_length + session->local.mac->mac_len); + if (!encbuf) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate encryption buffer", 0); + if (free_data) { + LIBSSH2_FREE(session, data); + } + return -1; + } + + /* Copy packet to encoding buffer */ + memcpy(encbuf, buf, 5); + memcpy(encbuf + 5, data, data_len); + RAND_bytes(encbuf + 5 + data_len, padding_length); + if (free_data) { + LIBSSH2_FREE(session, data); + } + + /* Calculate MAC hash */ + session->local.mac->hash(session, encbuf + 4 + packet_length , session->local.seqno, encbuf, 4 + packet_length, NULL, 0, &session->local.mac_abstract); + + /* Encrypt data */ + for(s = encbuf; (s - encbuf) < (int) (4 + packet_length) ; s += session->local.crypt->blocksize) { + if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + EVP_Cipher(ctx, buf, s, session->local.crypt->blocksize); + memcpy(s, buf, session->local.crypt->blocksize); + } else { + session->local.crypt->crypt(session, s, &session->local.crypt_abstract); + } + } + + session->local.seqno++; + + /* Send It */ + ret = ((4 + packet_length + session->local.mac->mac_len) == (unsigned int) send(session->socket_fd, (char *)encbuf, 4 + packet_length + session->local.mac->mac_len, LIBSSH2_SOCKET_SEND_FLAGS(session))) ? 0 : -1; + + /* Cleanup environment */ + LIBSSH2_FREE(session, encbuf); + + return ret; + } else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */ + /* Simplified write for non-encrypted mode */ + struct iovec data_vector[3]; + + /* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned + * No MAC during unencrypted phase + */ + data_vector[0].iov_base = (char *)buf; + data_vector[0].iov_len = 5; + data_vector[1].iov_base = (char*)data; + data_vector[1].iov_len = data_len; + data_vector[2].iov_base = (char *)buf + 5; + data_vector[2].iov_len = padding_length; + + session->local.seqno++; + + /* Ignore this, it can't actually happen :) */ + if (free_data) { + LIBSSH2_FREE(session, data); + } + + return ((packet_length + 4) == (unsigned int)writev(session->socket_fd, data_vector, 3)) ? 0 : 1; + } +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/libssh2/publickey.c b/pandora_agents/win32/ssh/libssh2/publickey.c new file mode 100644 index 0000000000..4d16a73800 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/publickey.c @@ -0,0 +1,728 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include "libssh2_publickey.h" + +struct _LIBSSH2_PUBLICKEY { + LIBSSH2_CHANNEL *channel; + unsigned long version; +}; + +#define LIBSSH2_PUBLICKEY_VERSION 2 + +/* Numericised response codes -- Not IETF standard, just a local representation */ +#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0 +#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1 +#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2 + +typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST { + int code; + char *name; + int name_len; +} LIBSSH2_PUBLICKEY_CODE_LIST; + +static LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_response_codes[] = { + { LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1 }, + { LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1 }, + { LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey", sizeof("publickey") - 1 }, + { 0, NULL, 0 } +}; + +/* PUBLICKEY status codes -- IETF defined */ +#define LIBSSH2_PUBLICKEY_SUCCESS 0 +#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1 +#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2 +#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3 +#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4 +#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5 +#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6 +#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7 +#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8 + +#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8 + +static LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes[] = { + { LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1 }, + { LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied", sizeof("access denied") - 1 }, + { LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded", sizeof("storage exceeded") - 1 }, + { LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported", sizeof("version not supported") - 1 }, + { LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found", sizeof("key not found") - 1 }, + { LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported", sizeof("key not supported") - 1 }, + { LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present", sizeof("key already present") - 1 }, + { LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure", sizeof("general failure") - 1 }, + { LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported", sizeof("request not supported") - 1 }, + { 0, NULL, 0 } +}; + +/* {{{ libssh2_publickey_status_error + * Format an error message from a status code + */ +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_START "Publickey Subsystem Error: \"" +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_MID "\" Server Resports: \"" +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_END "\"" +static void libssh2_publickey_status_error(LIBSSH2_PUBLICKEY *pkey, LIBSSH2_SESSION *session, int status, unsigned char *message, int message_len) +{ + char *status_text; + int status_text_len; + char *m, *s; + int m_len; + + /* GENERAL_FAILURE got remapped between version 1 and 2 */ + if (status == 6 && pkey && pkey->version == 1) { + status = 7; + } + + if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) { + status_text = "unknown"; + status_text_len = sizeof("unknown") - 1; + } else { + status_text = libssh2_publickey_status_codes[status].name; + status_text_len = libssh2_publickey_status_codes[status].name_len; + } + + m_len = (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1) + status_text_len + + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1) + message_len + + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); + m = (char *)LIBSSH2_ALLOC(session, m_len + 1); + if (!m) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for status message", 0); + return; + } + s = m; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_START, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1; + memcpy(s, status_text, status_text_len); s += status_text_len; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_MID, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1; + memcpy(s, message, message_len); s += message_len; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_END, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, m, 1); +} +/* }}} */ + +/* {{{ libssh2_publickey_packet_receive + * Read a packet from the subsystem + */ +static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned char **data, unsigned long *data_len) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char buffer[4]; + unsigned long packet_len; + unsigned char *packet; + + if (libssh2_channel_read(channel,(char *) buffer, 4) != 4) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0); + return -1; + } + + packet_len = libssh2_ntohu32(buffer); + packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate publickey response buffer", 0); + return -1; + } + + if (libssh2_channel_read(channel, (char *)packet, packet_len) != (int)packet_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + + *data = packet; + *data_len = packet_len; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_publickey_response_id + * Translate a string response name to a numeric code + * Data will be incremented by 4 + response_len on success only + */ +static int libssh2_publickey_response_id(unsigned char **pdata, int data_len) +{ + unsigned long response_len; + unsigned char *data = *pdata; + LIBSSH2_PUBLICKEY_CODE_LIST *codes = libssh2_publickey_response_codes; + + if (data_len < 4) { + /* Malformed response */ + return -1; + } + response_len = libssh2_ntohu32(data); data += 4; data_len -= 4; + if (data_len < (int) response_len) { + /* Malformed response */ + return -1; + } + + while (codes->name) { + if (codes->name_len == (int)response_len && + strncmp((char *)codes->name, (char *)data, response_len) == 0) { + *pdata = data + response_len; + return codes->code; + } + codes++; + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_publickey_response_success + * Generic helper routine to wait for success response and nothing else + */ +static int libssh2_publickey_response_success(LIBSSH2_PUBLICKEY *pkey) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + unsigned char *data, *s; + unsigned long data_len, response; + + while (1) { + if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); + return -1; + } + + s = data; + if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); + LIBSSH2_FREE(session, data); + return -1; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error, or processing complete */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); s += 4; + descr_len = libssh2_ntohu32(s); s += 4; + descr = s; s += descr_len; + lang_len = libssh2_ntohu32(s); s += 4; + lang = s; s += lang_len; + + if (s > data + data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); + LIBSSH2_FREE(session, data); + return -1; + } + + if (status == LIBSSH2_PUBLICKEY_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + libssh2_publickey_status_error(pkey, session, status, descr, descr_len); + LIBSSH2_FREE(session, data); + return -1; + } + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); + LIBSSH2_FREE(session, data); + data = NULL; + } + } + /* never reached, but include `return` to silence compiler warnings */ + return -1; +} +/* }}} */ + + +/* ***************** + * Publickey API * + ***************** */ + +/* {{{ libssh2_publickey_init + * Startup the publickey subsystem + */ +LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session) +{ + LIBSSH2_PUBLICKEY *pkey = NULL; + LIBSSH2_CHANNEL *channel = NULL; + unsigned char buffer[19]; + /* packet_len(4) + + version_len(4) + + "version"(7) + + version_num(4) */ + unsigned char *s, *data = NULL; + unsigned long data_len; + int response; + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem"); +#endif + + channel = libssh2_channel_open_session(session); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); + goto err_exit; + } + if (libssh2_channel_subsystem(channel, "publickey")) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0); + goto err_exit; + } + + libssh2_channel_set_blocking(channel, 1); + libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + + pkey = (LIBSSH2_PUBLICKEY *)LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); + if (!pkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0); + goto err_exit; + } + pkey->channel = channel; + pkey->version = 0; + + s = buffer; + libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4; + libssh2_htonu32(s, sizeof("version") - 1); s += 4; + memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1; + libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4; + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", (int)LIBSSH2_PUBLICKEY_VERSION); +#endif + if ((s - buffer) != libssh2_channel_write(channel, (char *)buffer, (s - buffer))) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0); + goto err_exit; + } + + while (1) { + if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); + goto err_exit; + } + + s = data; + if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); + goto err_exit; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); s += 4; + descr_len = libssh2_ntohu32(s); s += 4; + descr = s; s += descr_len; + lang_len = libssh2_ntohu32(s); s += 4; + lang = s; s += lang_len; + + if (s > data + data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); + goto err_exit; + } + + libssh2_publickey_status_error(NULL, session, status, descr, descr_len); + goto err_exit; + } + case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: + /* What we want */ + pkey->version = libssh2_ntohu32(s); + if (pkey->version > LIBSSH2_PUBLICKEY_VERSION) { +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", pkey->version); +#endif + pkey->version = LIBSSH2_PUBLICKEY_VERSION; + } +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", pkey->version); +#endif + LIBSSH2_FREE(session, data); + return pkey; + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); + LIBSSH2_FREE(session, data); + data = NULL; + } + } + + /* Never reached except by direct goto */ + err_exit: + if (channel) { + libssh2_channel_close(channel); + } + if (pkey) { + LIBSSH2_FREE(session, pkey); + } + if (data) { + LIBSSH2_FREE(session, data); + } + return NULL; +} +/* }}} */ + +/* {{{ libssh2_publickey_add_ex + * Add a new public key entry + */ +LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len, char overwrite, + unsigned long num_attrs, libssh2_publickey_attribute attrs[]) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char *packet = NULL, *s; + unsigned long i, packet_len = 19 + name_len + blob_len; + unsigned char *comment = NULL; + unsigned long comment_len = 0; + /* packet_len(4) + + add_len(4) + + "add"(3) + + name_len(4) + + {name} + blob_len(4) + + {blob} */ + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s pubickey", name); +#endif + + if (pkey->version == 1) { + for(i = 0; i < num_attrs; i++) { + /* Search for a comment attribute */ + if (attrs[i].name_len == (sizeof("comment") - 1) && + strncmp(attrs[i].name, "comment", sizeof("comment") - 1) == 0) { + comment = (unsigned char *)attrs[i].value; + comment_len = attrs[i].value_len; + break; + } + } + packet_len += 4 + comment_len; + } else { + packet_len += 5; /* overwrite(1) + attribute_count(4) */ + for(i = 0; i < num_attrs; i++) { + packet_len += 9 + attrs[i].name_len + attrs[i].value_len; + /* name_len(4) + value_len(4) + mandatory(1) */ + } + } + + packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"add\" packet", 0); + return -1; + } + + s = packet; + libssh2_htonu32(s, packet_len - 4); s += 4; + libssh2_htonu32(s, sizeof("add") - 1); s += 4; + memcpy(s, "add", sizeof("add") - 1); s += sizeof("add") - 1; + if (pkey->version == 1) { + libssh2_htonu32(s, comment_len); s += 4; + if (comment) { + memcpy(s, comment, comment_len); s += comment_len; + } + + libssh2_htonu32(s, name_len); s += 4; + memcpy(s, name, name_len); s += name_len; + libssh2_htonu32(s, blob_len); s += 4; + memcpy(s, blob, blob_len); s += blob_len; + } else { + /* Version == 2 */ + + libssh2_htonu32(s, name_len); s += 4; + memcpy(s, name, name_len); s += name_len; + libssh2_htonu32(s, blob_len); s += 4; + memcpy(s, blob, blob_len); s += blob_len; + *(s++) = overwrite ? 0xFF : 0; + libssh2_htonu32(s, num_attrs); s += 4; + for(i = 0; i < num_attrs; i++) { + libssh2_htonu32(s, attrs[i].name_len); s += 4; + memcpy(s, attrs[i].name, attrs[i].name_len); s += attrs[i].name_len; + libssh2_htonu32(s, attrs[i].value_len); s += 4; + memcpy(s, attrs[i].value, attrs[i].value_len); s += attrs[i].value_len; + *(s++) = attrs[i].mandatory ? 0xFF : 0; + } + } + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", name, blob_len, num_attrs); +#endif + if ((s - packet) != libssh2_channel_write(channel, (char *)packet, (s - packet))) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey add packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + packet = NULL; + + return libssh2_publickey_response_success(pkey); +} +/* }}} */ + +/* {{{ libssh2_publickey_remove_ex + * Remove an existing publickey so that authentication can no longer be performed using it + */ +LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *packet = NULL; + unsigned long packet_len = 22 + name_len + blob_len; + /* packet_len(4) + + remove_len(4) + + "remove"(6) + + name_len(4) + + {name} + blob_len(4) + + {blob} */ + + packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"remove\" packet", 0); + return -1; + } + + s = packet; + libssh2_htonu32(s, packet_len - 4); s += 4; + libssh2_htonu32(s, sizeof("remove") - 1); s += 4; + memcpy(s, "remove", sizeof("remove") - 1); s += sizeof("remove") - 1; + libssh2_htonu32(s, name_len); s += 4; + memcpy(s, name, name_len); s += name_len; + libssh2_htonu32(s, blob_len); s += 4; + memcpy(s, blob, blob_len); s += blob_len; + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"remove\" packet: type=%s blob_len=%ld", name, blob_len); +#endif + if ((s - packet) != libssh2_channel_write(channel, (char *)packet, (s - packet))) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey remove packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + packet = NULL; + + return libssh2_publickey_response_success(pkey); +} +/* }}} */ + +/* {{{ libssh2_publickey_list_fetch + * Fetch a list of supported public key from a server + */ +LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + libssh2_publickey_list *list = NULL; + unsigned char *s, buffer[12], *data = NULL; + unsigned long buffer_len = 12, keys = 0, max_keys = 0, data_len, i, response; + /* packet_len(4) + + list_len(4) + + "list"(4) */ + + s = buffer; + libssh2_htonu32(s, buffer_len - 4); s += 4; + libssh2_htonu32(s, sizeof("list") - 1); s += 4; + memcpy(s, "list", sizeof("list") - 1); s += sizeof("list") - 1; + +#ifdef LIBSSH2_DEBUG_PUBLICKEY + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet"); +#endif + if ((s - buffer) != libssh2_channel_write(channel, (char *)buffer, (s - buffer))) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0); + return -1; + } + + while (1) { + if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0); + goto err_exit; + } + + s = data; + if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0); + goto err_exit; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error, or processing complete */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); s += 4; + descr_len = libssh2_ntohu32(s); s += 4; + descr = s; s += descr_len; + lang_len = libssh2_ntohu32(s); s += 4; + lang = s; s += lang_len; + + if (s > data + data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0); + goto err_exit; + } + + if (status == LIBSSH2_PUBLICKEY_SUCCESS) { + LIBSSH2_FREE(session, data); + *pkey_list = list; + *num_keys = keys; + return 0; + } + + libssh2_publickey_status_error(pkey, session, status, descr, descr_len); + goto err_exit; + } + case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY: + /* What we want */ + if (keys >= max_keys) { + /* Grow the key list if necessary */ + max_keys += 8; + list = (libssh2_publickey_list *)LIBSSH2_REALLOC(session, list, (max_keys + 1) * sizeof(libssh2_publickey_list)); + if (!list) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey list", 0); + goto err_exit; + } + } + if (pkey->version == 1) { + unsigned long comment_len; + + comment_len = libssh2_ntohu32(s); s += 4; + if (comment_len) { + list[keys].num_attrs = 1; + list[keys].attrs = (libssh2_publickey_attribute *)LIBSSH2_ALLOC(session, sizeof(libssh2_publickey_attribute)); + if (!list[keys].attrs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); + goto err_exit; + } + list[keys].attrs[0].name = "comment"; + list[keys].attrs[0].name_len = sizeof("comment") - 1; + list[keys].attrs[0].value = (char *)s; + list[keys].attrs[0].value_len = comment_len; + list[keys].attrs[0].mandatory = 0; + + s += comment_len; + } else { + list[keys].num_attrs = 0; + list[keys].attrs = NULL; + } + list[keys].name_len = libssh2_ntohu32(s); s += 4; + list[keys].name = s; s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(s); s += 4; + list[keys].blob = s; s += list[keys].blob_len; + } else { + /* Version == 2 */ + list[keys].name_len = libssh2_ntohu32(s); s += 4; + list[keys].name = s; s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(s); s += 4; + list[keys].blob = s; s += list[keys].blob_len; + list[keys].num_attrs = libssh2_ntohu32(s); s += 4; + if (list[keys].num_attrs) { + list[keys].attrs = (libssh2_publickey_attribute *)LIBSSH2_ALLOC(session, list[keys].num_attrs * sizeof(libssh2_publickey_attribute)); + if (!list[keys].attrs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0); + goto err_exit; + } + for(i = 0; i < list[keys].num_attrs; i++) { + list[keys].attrs[i].name_len = libssh2_ntohu32(s); s += 4; + list[keys].attrs[i].name = (char *)s; s += list[keys].attrs[i].name_len; + list[keys].attrs[i].value_len = libssh2_ntohu32(s); s += 4; + list[keys].attrs[i].value = (char *)s; s += list[keys].attrs[i].value_len; + list[keys].attrs[i].mandatory = 0; /* actually an ignored value */ + } + } else { + list[keys].attrs = NULL; + } + } + list[keys].packet = data; /* To be FREEd in libssh2_publickey_list_free() */ + keys++; + + list[keys].packet = NULL; /* Terminate the list */ + data = NULL; + break; + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0); + LIBSSH2_FREE(session, data); + } + } + + /* Only reached via explicit goto */ + err_exit: + if (data) { + LIBSSH2_FREE(session, data); + } + if (list) { + libssh2_publickey_list_free(pkey, list); + } + return -1; +} +/* }}} */ + +/* {{{ libssh2_publickey_list_free + * Free a previously fetched list of public keys + */ +LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + libssh2_publickey_list *p = pkey_list; + + while (p->packet) { + if (p->attrs) { + LIBSSH2_FREE(session, p->attrs); + } + LIBSSH2_FREE(session, p->packet); + p++; + } + + LIBSSH2_FREE(session, pkey_list); +} +/* }}} */ + +/* {{{ libssh2_publickey_shutdown + * Shutdown the publickey subsystem + */ +LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + + libssh2_channel_free(pkey->channel); + LIBSSH2_FREE(session, pkey); +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/libssh2/scp.c b/pandora_agents/win32/ssh/libssh2/scp.c new file mode 100644 index 0000000000..a4ab912747 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/scp.c @@ -0,0 +1,437 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#include + +#define LIBSSH2_SCP_RESPONSE_BUFLEN 256 + +/* {{{ libssh2_scp_recv + * Open a channel and request a remote file via SCP + */ +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb) +{ + int path_len = strlen(path); + unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long command_len = path_len + sizeof("scp -f "), response_len; + LIBSSH2_CHANNEL *channel; + long mode = 0, size = 0, mtime = 0, atime = 0; + + if (sb) { + command_len++; + } + + command = (unsigned char *) LIBSSH2_ALLOC(session, command_len); + if (!command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); + return NULL; + } + if (sb) { + memcpy(command, "scp -pf ", sizeof("scp -pf ") - 1); + memcpy(command + sizeof("scp -pf ") - 1, path, path_len); + } else { + memcpy(command, "scp -f ", sizeof("scp -f ") - 1); + memcpy(command + sizeof("scp -f ") - 1, path, path_len); + } + command[command_len - 1] = '\0'; + +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive"); +#endif + /* Allocate a channel */ + if ((channel = libssh2_channel_open_session(session)) == NULL) { + LIBSSH2_FREE(session, command); + return NULL; + } + /* Use blocking I/O for negotiation phase */ + libssh2_channel_set_blocking(channel, 1); + + /* Request SCP for the desired file */ + if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, (char *)command, command_len)) { + LIBSSH2_FREE(session, command); + libssh2_channel_free(channel); + return NULL; + } + LIBSSH2_FREE(session, command); + +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup"); +#endif + /* SCP ACK */ + response[0] = '\0'; + if (libssh2_channel_write(channel, (char *)response, 1) != 1) { + libssh2_channel_free(channel); + return NULL; + } + + /* Parse SCP response */ + response_len = 0; + while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) { + unsigned char *s, *p; + + if (libssh2_channel_read(channel, (char *)response + response_len, 1) <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); + libssh2_channel_free(channel); + return NULL; + } + response_len++; + + if (response[0] != 'T') { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0); + libssh2_channel_free(channel); + return NULL; + } + + if ((response_len > 1) && + ((response[response_len-1] < '0') || (response[response_len-1] > '9')) && + (response[response_len-1] != ' ') && + (response[response_len-1] != '\r') && + (response[response_len-1] != '\n')) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); + libssh2_channel_free(channel); + return NULL; + } + + if ((response_len < 9) || (response[response_len-1] != '\n')) { + if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); + libssh2_channel_free(channel); + return NULL; + } + /* Way too short to be an SCP response, or not done yet, short circuit */ + continue; + } + + /* We're guaranteed not to go under response_len == 0 by the logic above */ + while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--; + response[response_len] = '\0'; + + if (response_len < 8) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); + libssh2_channel_free(channel); + return NULL; + } + + s = response + 1; + + p = (unsigned char *) strchr((char *)s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0); + libssh2_channel_free(channel); + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + mtime = strtol((char *)s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0); + libssh2_channel_free(channel); + return NULL; + } + s = (unsigned char *)strchr((char *)p, ' '); + if (!s || ((s - p) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0); + libssh2_channel_free(channel); + return NULL; + } + + /* Ignore mtime.usec */ + s++; + p = (unsigned char *)strchr((char *)s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); + libssh2_channel_free(channel); + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + atime = strtol((char *)s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0); + libssh2_channel_free(channel); + return NULL; + } + + /* SCP ACK */ + response[0] = '\0'; + if (libssh2_channel_write(channel, (char *)response, 1) != 1) { + libssh2_channel_free(channel); + return NULL; + } +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", mtime, atime); +#endif + + /* We *should* check that atime.usec is valid, but why let that stop use? */ + break; + } + + response_len = 0; + while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { + char *s, *p, *e = NULL; + + if (libssh2_channel_read(channel, (char *)response + response_len, 1) <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0); + libssh2_channel_free(channel); + return NULL; + } + response_len++; + + if (response[0] != 'C') { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0); + libssh2_channel_free(channel); + return NULL; + } + + if ((response_len > 1) && + (response[response_len-1] != '\r') && + (response[response_len-1] != '\n') && + ((response[response_len-1] < 32) || (response[response_len-1] > 126))) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0); + libssh2_channel_free(channel); + return NULL; + } + + if ((response_len < 7) || (response[response_len-1] != '\n')) { + if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0); + libssh2_channel_free(channel); + return NULL; + } + /* Way too short to be an SCP response, or not done yet, short circuit */ + continue; + } + + /* We're guaranteed not to go under response_len == 0 by the logic above */ + while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--; + response[response_len] = '\0'; + + if (response_len < 6) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0); + libssh2_channel_free(channel); + return NULL; + } + + s = (char *)response + 1; + + p = strchr(s, ' '); + if (!p || ((p - s) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0); + libssh2_channel_free(channel); + return NULL; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + mode = strtol(s, &e, 8); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0); + libssh2_channel_free(channel); + return NULL; + } + + s = strchr(p, ' '); + if (!s || ((s - p) <= 0)) { + /* No spaces or space in the wrong spot */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0); + libssh2_channel_free(channel); + return NULL; + } + + *(s++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + size = strtol(p, &e, 10); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0); + libssh2_channel_free(channel); + return NULL; + } + + /* SCP ACK */ + response[0] = '\0'; + if (libssh2_channel_write(channel, (char *)response, 1) != 1) { + libssh2_channel_free(channel); + return NULL; + } +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "mod = 0%lo size = %ld", mode, size); +#endif + + /* We *should* check that basename is valid, but why let that stop us? */ + break; + } + + if (sb) { + memset(sb, 0, sizeof(struct stat)); + + sb->st_mtime = mtime; + sb->st_atime = atime; + sb->st_size = size; + sb->st_mode = mode; + } + /* Revert to non-blocking and let the data BEGIN! */ + libssh2_channel_set_blocking(channel, 0); + + return channel; +} +/* }}} */ + +/* {{{ libssh2_scp_send_ex + * Send a file using SCP + */ +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime) +{ + int path_len = strlen(path); + unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long response_len, command_len = path_len + sizeof("scp -t "); + unsigned const char *base; + LIBSSH2_CHANNEL *channel; + + if (mtime || atime) { + command_len++; + } + + command = (unsigned char *)LIBSSH2_ALLOC(session, command_len); + if (!command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0); + return NULL; + } + + if (mtime || atime) { + memcpy(command, "scp -pt ", sizeof("scp -pt ") - 1); + memcpy(command + sizeof("scp -pt ") - 1, path, path_len); + } else { + memcpy(command, "scp -t ", sizeof("scp -t ") - 1); + memcpy(command + sizeof("scp -t ") - 1, path, path_len); + } + command[command_len - 1] = '\0'; + +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send"); +#endif + /* Allocate a channel */ + if ((channel = libssh2_channel_open_session(session)) == NULL) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, command); + return NULL; + } + /* Use blocking I/O for negotiation phase */ + libssh2_channel_set_blocking(channel, 1); + + /* Request SCP for the desired file */ + if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, (char *)command, command_len)) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, command); + libssh2_channel_free(channel); + return NULL; + } + LIBSSH2_FREE(session, command); + + /* Wait for ACK */ + if ((libssh2_channel_read(channel, (char *)response, 1) <= 0) || (response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(channel); + return NULL; + } + + /* Send mtime and atime to be used for file */ + if (mtime || atime) { + response_len = snprintf((char *)response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime); +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response); +#endif + if (libssh2_channel_write(channel, (char *)response, response_len) != (int)response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0); + libssh2_channel_free(channel); + return NULL; + } + /* Wait for ACK */ + if ((libssh2_channel_read(channel, (char *)response, 1) <= 0) || (response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(channel); + return NULL; + } + } + + /* Send mode, size, and basename */ + base = (unsigned char *)strrchr((char *)path, '/'); + if (base) { + base++; + } else { + base = (unsigned char *)path; + } + + response_len = snprintf((char *)response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base); +#ifdef LIBSSH2_DEBUG_SCP + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response); +#endif + if (libssh2_channel_write(channel, (char *)response, response_len) != (int)response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0); + libssh2_channel_free(channel); + return NULL; + } + /* Wait for ACK */ + if ((libssh2_channel_read(channel, (char *)response, 1) <= 0) || (response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0); + libssh2_channel_free(channel); + return NULL; + } + + /* Ready to start, switch to non-blocking and let calling app send file */ + libssh2_channel_set_blocking(channel, 0); + + return channel; +} +/* }}} */ + diff --git a/pandora_agents/win32/ssh/libssh2/session.c b/pandora_agents/win32/ssh/libssh2/session.c new file mode 100644 index 0000000000..f69a17a586 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/session.c @@ -0,0 +1,966 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include +#ifndef WIN32 +#include +#endif +#include + +#ifdef HAVE_GETTIMEOFDAY +#include +#include +#endif + +#ifdef HAVE_POLL +# include +#else +# ifdef HAVE_SELECT +# ifdef HAVE_SYS_SELECT_H +# include +# else +# include +# include +# endif +# endif +#endif + + + +/* {{{ libssh2_default_alloc + */ +static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc) +{ + return malloc(count); +} +/* }}} */ + +/* {{{ libssh2_default_free + */ +static LIBSSH2_FREE_FUNC(libssh2_default_free) +{ + free(ptr); +} +/* }}} */ + +/* {{{ libssh2_default_realloc + */ +static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc) +{ + return realloc(ptr, count); +} +/* }}} */ + +/* {{{ libssh2_banner_receive + * Wait for a hello from the remote host + * Allocate a buffer and store the banner in session->remote.banner + * Returns: 0 on success, 1 on failure + */ +static int libssh2_banner_receive(LIBSSH2_SESSION *session) +{ + char banner[256]; + int banner_len = 0; + + while ((banner_len < (int) sizeof(banner)) && + ((banner_len == 0) || (banner[banner_len-1] != '\n'))) { + char c = '\0'; + int ret; + + ret = recv(session->socket_fd, &c, 1, LIBSSH2_SOCKET_RECV_FLAGS(session)); + + if ((ret < 0) && (ret != EAGAIN)) { + /* Some kinda error, but don't break for non-blocking issues */ + return 1; + } + + if (ret <= 0) continue; + + if (c == '\0') { + /* NULLs are not allowed in SSH banners */ + return 1; + } + + banner[banner_len++] = c; + } + + while (banner_len && + ((banner[banner_len-1] == '\n') || (banner[banner_len-1] == '\r'))) { + banner_len--; + } + + if (!banner_len) return 1; + + session->remote.banner = (unsigned char *) LIBSSH2_ALLOC(session, banner_len + 1); + memcpy(session->remote.banner, banner, banner_len); + session->remote.banner[banner_len] = '\0'; +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", session->remote.banner); +#endif + return 0; +} +/* }}} */ + +/* {{{ libssh2_banner_send + * Send the default banner, or the one set via libssh2_setopt_string + */ +static int libssh2_banner_send(LIBSSH2_SESSION *session) +{ + char *banner = LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF; + int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1; + + if (session->local.banner) { + /* setopt_string will have given us our \r\n characters */ + banner_len = strlen((char *) session->local.banner); + banner = (char *) session->local.banner; + } +#ifdef LIBSSH2_DEBUG_TRANSPORT +{ + /* Hack and slash to avoid sending CRLF in debug output */ + char banner_dup[256]; + + if (banner_len < 256) { + memcpy(banner_dup, banner, banner_len - 2); + banner_dup[banner_len - 2] = '\0'; + } else { + memcpy(banner_dup, banner, 255); + banner[255] = '\0'; + } + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup); +} +#endif + + return (send(session->socket_fd, banner, banner_len, LIBSSH2_SOCKET_SEND_FLAGS(session)) == banner_len) ? 0 : 1; +} +/* }}} */ + +/* {{{ libssh2_banner_set + * Set the local banner + */ +LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner) +{ + int banner_len = banner ? strlen(banner) : 0; + + if (session->local.banner) { + LIBSSH2_FREE(session, session->local.banner); + session->local.banner = NULL; + } + + if (!banner_len) { + return 0; + } + + session->local.banner = (unsigned char *) LIBSSH2_ALLOC(session, banner_len + 3); + if (!session->local.banner) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for local banner", 0); + return -1; + } + + memcpy(session->local.banner, banner, banner_len); +#ifdef LIBSSH2_DEBUG_TRANSPORT + session->local.banner[banner_len] = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner); +#endif + session->local.banner[banner_len++] = '\r'; + session->local.banner[banner_len++] = '\n'; + session->local.banner[banner_len++] = '\0'; + + return 0; +} +/* }}} */ + +/* {{{ proto libssh2_session_init + * Allocate and initialize a libssh2 session structure + * Allows for malloc callbacks in case the calling program has its own memory manager + * It's allowable (but unadvisable) to define some but not all of the malloc callbacks + * An additional pointer value may be optionally passed to be sent to the callbacks (so they know who's asking) + */ +LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex( + LIBSSH2_ALLOC_FUNC((*my_alloc)), + LIBSSH2_FREE_FUNC((*my_free)), + LIBSSH2_REALLOC_FUNC((*my_realloc)), + void *abstract) +{ + LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc; + LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free; + LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc; + LIBSSH2_SESSION *session; + + if (my_alloc) local_alloc = my_alloc; + if (my_free) local_free = my_free; + if (my_realloc) local_realloc = my_realloc; + + session = (LIBSSH2_SESSION *) local_alloc(sizeof(LIBSSH2_SESSION), (void **) abstract); + memset(session, 0, sizeof(LIBSSH2_SESSION)); + session->alloc = local_alloc; + session->free = local_free; + session->realloc = local_realloc; + session->abstract = abstract; +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated"); +#endif + + return session; +} +/* }}} */ + +/* {{{ libssh2_session_callback_set + * Set (or reset) a callback function + * Returns the prior address + */ +LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbtype, void *callback) +{ + void *oldcb; + + switch (cbtype) { + case LIBSSH2_CALLBACK_IGNORE: + oldcb = (void *) session->ssh_msg_ignore; + session->ssh_msg_ignore = (void (*) (LIBSSH2_SESSION*, const char *, int, void **))callback; + return oldcb; + break; + case LIBSSH2_CALLBACK_DEBUG: + oldcb = (void *) session->ssh_msg_debug; + session->ssh_msg_debug = (void (*) (LIBSSH2_SESSION*, int, const char *, int, const char *, int, void **))callback; + return oldcb; + break; + case LIBSSH2_CALLBACK_DISCONNECT: + oldcb = (void *) session->ssh_msg_disconnect; + session->ssh_msg_disconnect = (void (*) (LIBSSH2_SESSION*, int, const char *, int, const char *, int, void **))callback; + return oldcb; + break; + case LIBSSH2_CALLBACK_MACERROR: + oldcb = (void *) session->macerror; + session->macerror = (int (*) (LIBSSH2_SESSION*, const char *, int, void **))callback; + return oldcb; + break; + case LIBSSH2_CALLBACK_X11: + oldcb = (void *) session->x11; + session->x11 = (void (*) (LIBSSH2_SESSION*, LIBSSH2_CHANNEL*, char*, int, void **) )callback; + return oldcb; + break; + } +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype); +#endif + + return NULL; +} +/* }}} */ + +/* {{{ proto libssh2_session_startup + * session: LIBSSH2_SESSION struct allocated and owned by the calling program + * Returns: 0 on success, or non-zero on failure + * Any memory allocated by libssh2 will use alloc/realloc/free callbacks in session + * socket *must* be populated with an opened socket + */ +LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket) +{ + unsigned char *data; + unsigned long data_len; + unsigned char service[sizeof("ssh-userauth") + 5 - 1]; + unsigned long service_length; + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "session_startup for socket %d", socket); +#endif + if (socket <= 0) { + /* Did we forget something? */ + libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "No socket provided", 0); + return LIBSSH2_ERROR_SOCKET_NONE; + } + session->socket_fd = socket; + + /* TODO: Liveness check */ + if (libssh2_banner_send(session)) { + /* Unable to send banner? */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, "Error sending banner to remote host", 0); + return LIBSSH2_ERROR_BANNER_SEND; + } + + if (libssh2_banner_receive(session)) { + /* Unable to receive banner from remote */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, "Timeout waiting for banner", 0); + return LIBSSH2_ERROR_BANNER_NONE; + } + + if (libssh2_kex_exchange(session, 0)) { + libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0); + return LIBSSH2_ERROR_KEX_FAILURE; + } + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service"); +#endif + /* Request the userauth service */ + service[0] = SSH_MSG_SERVICE_REQUEST; + libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1); + memcpy(service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1); + if (libssh2_packet_write(session, service, sizeof("ssh-userauth") + 5 - 1)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0); + return LIBSSH2_ERROR_SOCKET_SEND; + } + + if (libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, &data, &data_len)) { + return LIBSSH2_ERROR_SOCKET_DISCONNECT; + } + service_length = libssh2_ntohu32(data + 1); + + if ((service_length != (sizeof("ssh-userauth") - 1)) || + strncmp((char *)"ssh-userauth", (char *)data + 5, service_length)) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid response received from server", 0); + return LIBSSH2_ERROR_PROTO; + } + LIBSSH2_FREE(session, data); + + return 0; +} +/* }}} */ + +/* {{{ proto libssh2_session_free + * Frees the memory allocated to the session + * Also closes and frees any channels attached to this session + */ +LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session) +{ +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", session->remote.banner); +#endif + while (session->channels.head) { + LIBSSH2_CHANNEL *tmp = session->channels.head; + + libssh2_channel_free(session->channels.head); + if (tmp == session->channels.head) { + /* channel_free couldn't do it's job, perform a messy cleanup */ + tmp = session->channels.head; + + /* unlink */ + session->channels.head = tmp->next; + + /* free */ + LIBSSH2_FREE(session, tmp); + + /* reverse linking isn't important here, we're killing the structure */ + } + } + + while (session->listeners) { + libssh2_channel_forward_cancel(session->listeners); + } + + if (session->state & LIBSSH2_STATE_NEWKEYS) { + /* hostkey */ + if (session->hostkey && session->hostkey->dtor) { + session->hostkey->dtor(session, &session->server_hostkey_abstract); + } + + /* Client to Server */ + /* crypt */ + if (session->local.crypt) { + if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + if (session->local.crypt_abstract) { + LIBSSH2_FREE(session, session->local.crypt_abstract); + session->local.crypt_abstract = NULL; + } + } else if (session->local.crypt->dtor) { + session->local.crypt->dtor(session, &session->local.crypt_abstract); + } + } + /* comp */ + if (session->local.comp && session->local.comp->dtor) { + session->local.comp->dtor(session, 1, &session->local.comp_abstract); + } + /* mac */ + if (session->local.mac && session->local.mac->dtor) { + session->local.mac->dtor(session, &session->local.mac_abstract); + } + + /* Server to Client */ + /* crypt */ + if (session->remote.crypt) { + if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) { + if (session->remote.crypt_abstract) { + LIBSSH2_FREE(session, session->remote.crypt_abstract); + session->remote.crypt_abstract = NULL; + } + } else if (session->remote.crypt->dtor) { + session->remote.crypt->dtor(session, &session->remote.crypt_abstract); + } + } + /* comp */ + if (session->remote.comp && session->remote.comp->dtor) { + session->remote.comp->dtor(session, 0, &session->remote.comp_abstract); + } + /* mac */ + if (session->remote.mac && session->remote.mac->dtor) { + session->remote.mac->dtor(session, &session->remote.mac_abstract); + } + + /* session_id */ + if (session->session_id) { + LIBSSH2_FREE(session, session->session_id); + } + } + + /* Free banner(s) */ + if (session->remote.banner) { + LIBSSH2_FREE(session, session->remote.banner); + } + if (session->local.banner) { + LIBSSH2_FREE(session, session->local.banner); + } + + /* Free preference(s) */ + if (session->kex_prefs) { + LIBSSH2_FREE(session, session->kex_prefs); + } + if (session->hostkey_prefs) { + LIBSSH2_FREE(session, session->hostkey_prefs); + } + + if (session->local.crypt_prefs) { + LIBSSH2_FREE(session, session->local.crypt_prefs); + } + if (session->local.mac_prefs) { + LIBSSH2_FREE(session, session->local.mac_prefs); + } + if (session->local.comp_prefs) { + LIBSSH2_FREE(session, session->local.comp_prefs); + } + if (session->local.lang_prefs) { + LIBSSH2_FREE(session, session->local.lang_prefs); + } + + if (session->remote.crypt_prefs) { + LIBSSH2_FREE(session, session->remote.crypt_prefs); + } + if (session->remote.mac_prefs) { + LIBSSH2_FREE(session, session->remote.mac_prefs); + } + if (session->remote.comp_prefs) { + LIBSSH2_FREE(session, session->remote.comp_prefs); + } + if (session->remote.lang_prefs) { + LIBSSH2_FREE(session, session->remote.lang_prefs); + } + + /* Cleanup any remaining packets */ + while (session->packets.head) { + LIBSSH2_PACKET *tmp = session->packets.head; + + /* unlink */ + session->packets.head = tmp->next; + + /* free */ + LIBSSH2_FREE(session, tmp->data); + LIBSSH2_FREE(session, tmp); + } + + LIBSSH2_FREE(session, session); +} +/* }}} */ + +/* {{{ libssh2_session_disconnect_ex + */ +LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang) +{ + unsigned char *s, *data; + unsigned long data_len, descr_len = 0, lang_len = 0; + +#ifdef LIBSSH2_DEBUG_TRANSPORT + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang); +#endif + if (description) { + descr_len = strlen(description); + } + if (lang) { + lang_len = strlen(lang); + } + data_len = descr_len + lang_len + 13; /* packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ + + s = data = (unsigned char *) LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for disconnect packet", 0); + return -1; + } + + *(s++) = SSH_MSG_DISCONNECT; + libssh2_htonu32(s, reason); s += 4; + + libssh2_htonu32(s, descr_len); s += 4; + if (description) { + memcpy(s, description, descr_len); + s += descr_len; + } + + libssh2_htonu32(s, lang_len); s += 4; + if (lang) { + memcpy(s, lang, lang_len); + s += lang_len; + } + + libssh2_packet_write(session, data, data_len); + + LIBSSH2_FREE(session, data); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_session_methods + * Return the currently active methods for method_type + * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string regardless of actual negotiation + * Strings should NOT be freed + */ +LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type) +{ + /* All methods have char *name as their first element */ + LIBSSH2_KEX_METHOD *method = NULL; + + switch(method_type) { + case LIBSSH2_METHOD_KEX: + method = session->kex; + break; + case LIBSSH2_METHOD_HOSTKEY: + method = (LIBSSH2_KEX_METHOD*)session->hostkey; + break; + case LIBSSH2_METHOD_CRYPT_CS: + method = (LIBSSH2_KEX_METHOD*)session->local.crypt; + break; + case LIBSSH2_METHOD_CRYPT_SC: + method = (LIBSSH2_KEX_METHOD*)session->remote.crypt; + break; + case LIBSSH2_METHOD_MAC_CS: + method = (LIBSSH2_KEX_METHOD*)session->local.mac; + break; + case LIBSSH2_METHOD_MAC_SC: + method = (LIBSSH2_KEX_METHOD*)session->remote.mac; + break; + case LIBSSH2_METHOD_COMP_CS: + method = (LIBSSH2_KEX_METHOD*)session->local.comp; + break; + case LIBSSH2_METHOD_COMP_SC: + method = (LIBSSH2_KEX_METHOD*)session->remote.comp; + break; + case LIBSSH2_METHOD_LANG_CS: + return ""; + break; + case LIBSSH2_METHOD_LANG_SC: + return ""; + break; + default: + libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid parameter specified for method_type", 0); + return NULL; + break; + } + + if (!method) { + libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, "No method negotiated", 0); + return NULL; + } + + return method->name; +} +/* }}} */ + +/* {{{ libssh2_session_abstract + * Retrieve a pointer to the abstract property + */ +LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session) +{ + return &session->abstract; +} +/* }}} */ + +/* {{{ libssh2_session_last_error + * Returns error code and populates an error string into errmsg + * If want_buf is non-zero then the string placed into errmsg must be freed by the calling program + * Otherwise it is assumed to be owned by libssh2 + */ +LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf) +{ + /* No error to report */ + if (!session->err_code) { + if (errmsg) { + if (want_buf) { + *errmsg = (char *) LIBSSH2_ALLOC(session, 1); + if (*errmsg) { + **errmsg = 0; + } + } else { + *errmsg = ""; + } + } + if (errmsg_len) { + *errmsg_len = 0; + } + return 0; + } + + if (errmsg) { + char *serrmsg = session->err_msg ? session->err_msg : (char *) ""; + int ownbuf = session->err_msg ? session->err_should_free : 0; + + if (want_buf) { + if (ownbuf) { + /* Just give the calling program the buffer */ + *errmsg = serrmsg; + session->err_should_free = 0; + } else { + /* Make a copy so the calling program can own it */ + *errmsg = (char *) LIBSSH2_ALLOC(session, session->err_msglen + 1); + if (*errmsg) { + memcpy(*errmsg, session->err_msg, session->err_msglen); + (*errmsg)[session->err_msglen] = 0; + } + } + } else { + *errmsg = serrmsg; + } + } + + if (errmsg_len) { + *errmsg_len = session->err_msglen; + } + + return session->err_code; +} +/* }}} */ + +/* {{{ libssh2_session_flag + * Set/Get session flags + * Passing flag==0 will avoid changing session->flags while still returning its current value + */ +LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int value) +{ + if (value) { + session->flags |= flag; + } else { + session->flags &= ~flag; + } + + return session->flags; +} +/* }}} */ + +/* {{{ libssh2_poll_channel_read + * Returns 0 if no data is waiting on channel, + * non-0 if data is available + */ +LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended) +{ + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_PACKET *packet = session->packets.head; + + while (packet) { + if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) && (extended == 0) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) || + ((packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (extended != 0) && (channel->local.id == libssh2_ntohu32(packet->data + 1)))) { + /* Found data waiting to be read */ + return 1; + } + packet = packet->next; + } + + return 0; +} +/* }}} */ + +/* {{{ libssh2_poll_channel_write + * Returns 0 if writing to channel would block, + * non-0 if data can be written without blocking + */ +inline int libssh2_poll_channel_write(LIBSSH2_CHANNEL *channel) +{ + return channel->local.window_size ? 1 : 0; +} +/* }}} */ + +/* {{{ libssh2_poll_listener_queued + * Returns 0 if no connections are waiting to be accepted + * non-0 if one or more connections are available + */ +inline int libssh2_poll_listener_queued(LIBSSH2_LISTENER *listener) +{ + return listener->queue ? 1 : 0; +} +/* }}} */ + +/* {{{ libssh2_poll + * Poll sockets, channels, and listeners for activity + */ +LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeout) +{ + long timeout_remaining; + int i, active_fds; +#ifdef HAVE_POLL + LIBSSH2_SESSION *session = NULL; + struct pollfd sockets[nfds]; + + /* Setup sockets for polling */ + for(i = 0; i < nfds; i++) { + fds[i].revents = 0; + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + sockets[i].fd = fds[i].fd.socket; + sockets[i].events = fds[i].events; + sockets[i].revents = 0; + break; + case LIBSSH2_POLLFD_CHANNEL: + sockets[i].fd = fds[i].fd.channel->session->socket_fd; + sockets[i].events = POLLIN; + sockets[i].revents = 0; + if (!session) session = fds[i].fd.channel->session; + break; + case LIBSSH2_POLLFD_LISTENER: + sockets[i].fd = fds[i].fd.listener->session->socket_fd; + sockets[i].events = POLLIN; + sockets[i].revents = 0; + if (!session) session = fds[i].fd.listener->session; + break; + default: + if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); + return -1; + } + } +#elif defined(HAVE_SELECT) + LIBSSH2_SESSION *session = NULL; + int maxfd = 0; + fd_set rfds,wfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + for(i = 0; i < nfds; i++) { + fds[i].revents = 0; + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + if (fds[i].events & LIBSSH2_POLLFD_POLLIN) { + FD_SET(fds[i].fd.socket, &rfds); + if (fds[i].fd.socket > maxfd) maxfd = fds[i].fd.socket; + } + if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) { + FD_SET(fds[i].fd.socket, &wfds); + if (fds[i].fd.socket > maxfd) maxfd = fds[i].fd.socket; + } + break; + case LIBSSH2_POLLFD_CHANNEL: + FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); + if (fds[i].fd.channel->session->socket_fd > maxfd) maxfd = fds[i].fd.channel->session->socket_fd; + if (!session) session = fds[i].fd.channel->session; + break; + case LIBSSH2_POLLFD_LISTENER: + FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); + if (fds[i].fd.listener->session->socket_fd > maxfd) maxfd = fds[i].fd.listener->session->socket_fd; + if (!session) session = fds[i].fd.listener->session; + break; + default: + if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); + return -1; + } + } +#else + /* No select() or poll() + * no sockets sturcture to setup + */ + + timeout = 0; +#endif /* HAVE_POLL or HAVE_SELECT */ + + timeout_remaining = timeout; + do { +#if defined(HAVE_POLL) || defined(HAVE_SELECT) + int sysret; +#endif + + active_fds = 0; + + for (i = 0; i < (int) nfds; i++) { + if (fds[i].events != fds[i].revents) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_CHANNEL: + if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want to be ready for read */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* Not yet known to be ready for read */ + fds[i].revents |= libssh2_poll_channel_read(fds[i].fd.channel, 0) ? LIBSSH2_POLLFD_POLLIN : 0; + } + if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) && /* Want to be ready for extended read */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) { /* Not yet known to be ready for extended read */ + fds[i].revents |= libssh2_poll_channel_read(fds[i].fd.channel, 1) ? LIBSSH2_POLLFD_POLLEXT : 0; + } + if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) && /* Want to be ready for write */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */ + fds[i].revents |= libssh2_poll_channel_write(fds[i].fd.channel) ? LIBSSH2_POLLFD_POLLOUT : 0; + } + if (fds[i].fd.channel->remote.close || fds[i].fd.channel->local.close) { + fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED; + } + if (fds[i].fd.channel->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { + fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; + } + break; + case LIBSSH2_POLLFD_LISTENER: + if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */ + fds[i].revents |= libssh2_poll_listener_queued(fds[i].fd.listener) ? LIBSSH2_POLLFD_POLLIN : 0; + } + if (fds[i].fd.listener->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { + fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; + } + break; + } + } + if (fds[i].revents) { + active_fds++; + } + } + + if (active_fds) { + /* Don't block on the sockets if we have channels/listeners which are ready */ + timeout_remaining = 0; + } + +#ifdef HAVE_POLL + +#ifdef HAVE_GETTIMEOFDAY +{ + struct timeval tv_begin, tv_end; + + gettimeofday((struct timeval *)&tv_begin, NULL); + sysret = poll(sockets, nfds, timeout_remaining); + gettimeofday((struct timeval *)&tv_end, NULL); + timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; + timeout_remaining -= ceil((tv_end.tv_usec - tv_begin.tv_usec) / 1000); +} +#else + /* If the platform doesn't support gettimeofday, + * then just make the call non-blocking and walk away + */ + sysret = poll(sockets, nfds, timeout_remaining); + timeout_remaining = 0; +#endif /* HAVE_GETTIMEOFDAY */ + + if (sysret > 0) { + for (i = 0; i < nfds; i++) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + fds[i].revents = sockets[i].revents; + sockets[i].revents = 0; /* In case we loop again, be nice */ + if (fds[i].revents) { + active_fds++; + } + break; + case LIBSSH2_POLLFD_CHANNEL: + if (sockets[i].events & POLLIN) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0); + } + if (sockets[i].revents & POLLHUP) { + fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; + } + sockets[i].revents = 0; + break; + case LIBSSH2_POLLFD_LISTENER: + if (sockets[i].events & POLLIN) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0); + } + if (sockets[i].revents & POLLHUP) { + fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; + } + sockets[i].revents = 0; + break; + } + } + } +#elif defined(HAVE_SELECT) + tv.tv_sec = timeout_remaining / 1000; + tv.tv_usec = (timeout_remaining % 1000) * 1000; +#ifdef HAVE_GETTIMEOFDAY +{ + struct timeval tv_begin, tv_end; + + gettimeofday((struct timeval *)&tv_begin, NULL); + sysret = select(maxfd, &rfds, &wfds, NULL, &tv); + gettimeofday((struct timeval *)&tv_end, NULL); + + timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; + timeout_remaining -= ceil((tv_end.tv_usec - tv_begin.tv_usec) / 1000); +} +#else + /* If the platform doesn't support gettimeofday, + * then just make the call non-blocking and walk away + */ + sysret = select(maxfd, &rfds, &wfds, NULL, &tv); + timeout_remaining = 0; +#endif + + if (sysret > 0) { + for (i = 0; i < nfds; i++) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + if (FD_ISSET(fds[i].fd.socket, &rfds)) { + fds[i].revents |= LIBSSH2_POLLFD_POLLIN; + } + if (FD_ISSET(fds[i].fd.socket, &wfds)) { + fds[i].revents |= LIBSSH2_POLLFD_POLLOUT; + } + if (fds[i].revents) { + active_fds++; + } + break; + case LIBSSH2_POLLFD_CHANNEL: + if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0); + } + break; + case LIBSSH2_POLLFD_LISTENER: + if (FD_ISSET(fds[i].fd.listener->session->socket_fd, &rfds)) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0); + } + break; + } + } + } +#endif /* else no select() or poll() -- timeout (and by extension timeout_remaining) will be equal to 0 */ + } while ((timeout_remaining > 0) && !active_fds); + + return active_fds; +} +/* }}} */ + diff --git a/pandora_agents/win32/ssh/libssh2/sftp.c b/pandora_agents/win32/ssh/libssh2/sftp.c new file mode 100644 index 0000000000..36d2a1b516 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/sftp.c @@ -0,0 +1,1473 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include "libssh2_sftp.h" + +/* Note: Version 6 was documented at the time of writing + * However it was marked as "DO NOT IMPLEMENT" due to pending changes + * + * This release of libssh2 implements Version 5 with automatic downgrade + * based on server's declaration + */ + +/* SFTP packet types */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_READLINK 19 +#define SSH_FXP_SYMLINK 20 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 + +struct _LIBSSH2_SFTP { + LIBSSH2_CHANNEL *channel; + + unsigned long request_id, version; + + LIBSSH2_PACKET_BRIGADE packets; + + LIBSSH2_SFTP_HANDLE *handles; + + unsigned long last_errno; +}; + +#define LIBSSH2_SFTP_HANDLE_FILE 0 +#define LIBSSH2_SFTP_HANDLE_DIR 1 + +/* S_IFREG */ +#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000 +/* S_IFDIR */ +#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 + +struct _LIBSSH2_SFTP_HANDLE { + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *prev, *next; + + char *handle; + int handle_len; + + char handle_type; + + union _libssh2_sftp_handle_data { + struct _libssh2_sftp_handle_file_data { + libssh2_uint64_t offset; + } file; + struct _libssh2_sftp_handle_dir_data { + unsigned long names_left; + void *names_packet; + char *next_name; + } dir; + } u; +}; + +/* {{{ libssh2_sftp_packet_add + * Add a packet to the SFTP packet brigade + */ +static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsigned long data_len) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d", (int)data[0]); +#endif + packet = (LIBSSH2_PACKET *)LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0); + return -1; + } + memset(packet, 0, sizeof(LIBSSH2_PACKET)); + + packet->data = data; + packet->data_len = data_len; + packet->data_head = 5; + packet->brigade = &sftp->packets; + packet->next = NULL; + packet->prev = sftp->packets.tail; + if (packet->prev) { + packet->prev->next = packet; + } else { + sftp->packets.head = packet; + } + sftp->packets.tail = packet; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_sftp_packet_read + * Frame an SFTP packet off the channel + */ +static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char buffer[4]; /* To store the packet length */ + unsigned char *packet; + unsigned long packet_len, packet_received; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot"); +#endif + if (should_block) { + libssh2_channel_set_blocking(channel, 1); + if (4 != libssh2_channel_read(channel, (char *)buffer, 4)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); + return -1; + } + } else { + libssh2_channel_set_blocking(channel, 0); + if (1 != libssh2_channel_read(channel, (char *)buffer, 1)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); + return 0; + } + libssh2_channel_set_blocking(channel, 1); + if (3 != libssh2_channel_read(channel, (char *)buffer + 1, 3)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); + return -1; + } + } + packet_len = libssh2_ntohu32(buffer); +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Data begin - Packet Length: %lu", packet_len); +#endif + if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0); + return -1; + } + + packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate SFTP packet", 0); + return -1; + } + + packet_received = 0; + while (packet_len > packet_received) { + long bytes_received = libssh2_channel_read(channel, (char *)packet + packet_received, packet_len - packet_received); + + if (bytes_received < 0) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + packet_received += bytes_received; + } + + if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { + LIBSSH2_FREE(session, packet); + return -1; + } + + return packet[0]; +} +/* }}} */ + +/* {{{ libssh2_sftp_packet_ask + * A la libssh2_packet_ask() + */ +static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len, int poll_channel) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet = sftp->packets.head; + unsigned char match_buf[5]; + int match_len = 5; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", (int)packet_type); +#endif + if (poll_channel) { + if (libssh2_sftp_packet_read(sftp, 0) < 0) { + return -1; + } + } + + match_buf[0] = packet_type; + if (packet_type == SSH_FXP_VERSION) { + /* Special consideration when matching VERSION packet */ + match_len = 1; + } else { + libssh2_htonu32(match_buf + 1, request_id); + } + + while (packet) { + if (strncmp((char *)packet->data, (char *)match_buf, match_len) == 0) { + *data = packet->data; + *data_len = packet->data_len; + + if (packet->prev) { + packet->prev->next = packet->next; + } else { + sftp->packets.head = packet->next; + } + + if (packet->next) { + packet->next->prev = packet->prev; + } else { + sftp->packets.tail = packet->prev; + } + + LIBSSH2_FREE(session, packet); + + return 0; + } + packet = packet->next; + } + return -1; +} +/* }}} */ + +/* {{{ libssh2_sftp_packet_require + * A la libssh2_packet_require + */ +static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type); +#endif + if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { + /* A packet was available in the packet brigade */ + return 0; + } + + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret = libssh2_sftp_packet_read(sftp, 1); + if (ret < 0) { + return -1; + } + if (ret == 0) continue; + + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; +} +/* }}} */ + +/* {{{ libssh2_sftp_packet_requirev + * Requie one of N possible reponses + */ +static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, unsigned char *valid_responses, unsigned long request_id, unsigned char **data, unsigned long *data_len) +{ + int i; + + /* Flush */ + while (libssh2_sftp_packet_read(sftp, 0) > 0); + + while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret; + for(i = 0; i < num_valid_responses; i++) { + if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, 0) == 0) { + return 0; + } + } + ret = libssh2_sftp_packet_read(sftp, 1); + if (ret < 0) { + return -1; + } + if (ret == 0) continue; + } + + return -1; +} +/* }}} */ + +/* {{{ libssh2_sftp_attrsize + * Size that attr will occupy when turned into a bin struct + */ +static int libssh2_sftp_attrsize(LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + int attrsize = 4; /* flags(4) */ + + if (!attrs) { + return attrsize; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) attrsize += 4; + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) attrsize += 8; /* atime + mtime as u32 */ + + return attrsize; +} +/* }}} */ + +/* {{{ libssh2_sftp_attr2bin + * Populate attributes into an SFTP block + */ +static int libssh2_sftp_attr2bin(unsigned char *p, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + unsigned char *s = p; + unsigned long flag_mask = LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID | LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME; + + /* TODO: When we add SFTP4+ functionality flag_mask can get additional bits */ + + if (!attrs) { + libssh2_htonu32(s, 0); + return 4; + } + + libssh2_htonu32(s, attrs->flags & flag_mask); s += 4; + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + libssh2_htonu64(s, attrs->filesize); s += 8; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + libssh2_htonu32(s, attrs->uid); s += 4; + libssh2_htonu32(s, attrs->gid); s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + libssh2_htonu32(s, attrs->permissions); s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + libssh2_htonu32(s, attrs->atime); s += 4; + libssh2_htonu32(s, attrs->mtime); s += 4; + } + + return (s - p); +} +/* }}} */ + +/* {{{ libssh2_sftp_bin2attr + */ +static int libssh2_sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, unsigned char *p) +{ + unsigned char *s = p; + + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + attrs->flags = libssh2_ntohu32(s); s += 4; + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + attrs->filesize = libssh2_ntohu64(s); s += 8; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + attrs->uid = libssh2_ntohu32(s); s += 4; + attrs->gid = libssh2_ntohu32(s); s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + attrs->permissions = libssh2_ntohu32(s); s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + attrs->atime = libssh2_ntohu32(s); s += 4; + attrs->mtime = libssh2_ntohu32(s); s += 4; + } + + return (s - p); +} +/* }}} */ + +/* ************ + * SFTP API * + ************ */ + +/* {{{ libssh2_sftp_dtor + * Shutdown an SFTP stream when the channel closes + */ +LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) { + LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP*)(*channel_abstract); + + /* Loop through handles closing them */ + while (sftp->handles) { + libssh2_sftp_close_handle(sftp->handles); + } + + LIBSSH2_FREE(session, sftp); +} +/* }}} */ + +/* {{{ libssh2_sftp_init + * Startup an SFTP session + */ +LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session) +{ + LIBSSH2_SFTP *sftp; + LIBSSH2_CHANNEL *channel; + unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ + unsigned long data_len; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); +#endif + channel = libssh2_channel_open_session(session); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0); + return NULL; + } + if (libssh2_channel_subsystem(channel, "sftp")) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request SFTP subsystem", 0); + libssh2_channel_free(channel); + return NULL; + } + + libssh2_channel_set_blocking(channel, 1); + + libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + + sftp = (LIBSSH2_SFTP *)LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); + if (!sftp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0); + libssh2_channel_free(channel); + return NULL; + } + memset(sftp, 0, sizeof(LIBSSH2_SFTP)); + sftp->channel = channel; + sftp->request_id = 0; + + libssh2_htonu32(buffer, 5); + buffer[4] = SSH_FXP_INIT; + libssh2_htonu32(buffer + 5, LIBSSH2_SFTP_VERSION); + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION); +#endif + if (9 != libssh2_channel_write(channel, (char *)buffer, 9)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } + if (data_len < 5) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid SSH_FXP_VERSION response", 0); + libssh2_channel_free(channel); + LIBSSH2_FREE(session, sftp); + return NULL; + } + + s = data + 1; + sftp->version = libssh2_ntohu32(s); s += 4; + if (sftp->version > LIBSSH2_SFTP_VERSION) { +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version); +#endif + sftp->version = LIBSSH2_SFTP_VERSION; + } +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version); +#endif + while (s < (data + data_len)) { + char *extension_name, *extension_data; + unsigned long extname_len, extdata_len; + + extname_len = libssh2_ntohu32(s); s += 4; + extension_name = (char *)s; s += extname_len; + + extdata_len = libssh2_ntohu32(s); s += 4; + extension_data = (char *)s; s += extdata_len; + + /* TODO: Actually process extensions */ + } + LIBSSH2_FREE(session, data); + + /* Make sure that when the channel gets closed, the SFTP service is shut down too */ + sftp->channel->abstract = sftp; + sftp->channel->close_cb = libssh2_sftp_dtor; + + return sftp; +} +/* }}} */ + +/* {{{ libssh2_sftp_shutdown + * Shutsdown the SFTP subsystem + */ +LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp) { + return libssh2_channel_free(sftp->channel); +} +/* }}} */ + +/* ******************************* + * SFTP File and Directory Ops * + ******************************* */ + +/* {{{ libssh2_sftp_open_ex + * + */ +LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *filename, int filename_len, unsigned long flags, long mode, int open_type) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_HANDLE *fp; + LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS }; + unsigned long data_len, packet_len = filename_len + 13 + ((open_type == LIBSSH2_SFTP_OPENFILE) ? (4 + libssh2_sftp_attrsize(&attrs)) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + flags(4) */ + unsigned char *packet, *data, *s; + unsigned char fopen_responses[2] = { SSH_FXP_HANDLE, SSH_FXP_STATUS }; + unsigned long request_id; + + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_OPEN or FXP_OPENDIR packet", 0); + return NULL; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | ((open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, filename_len); s += 4; + memcpy(s, filename, filename_len); s += filename_len; + if (open_type == LIBSSH2_SFTP_OPENFILE) { + libssh2_htonu32(s, flags); s += 4; + s += libssh2_sftp_attr2bin(s, &attrs); + } + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory"); +#endif + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_OPEN or FXP_OPENDIR command", 0); + LIBSSH2_FREE(session, packet); + return NULL; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return NULL; + } + + if (data[0] == SSH_FXP_STATUS) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Failed opening remote file", 0); + sftp->last_errno = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + return NULL; + } + + fp = (LIBSSH2_SFTP_HANDLE *)LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); + if (!fp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate new SFTP handle structure", 0); + LIBSSH2_FREE(session, data); + return NULL; + } + memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); + fp->handle_type = (open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_HANDLE_FILE : LIBSSH2_SFTP_HANDLE_DIR; + + fp->handle_len = libssh2_ntohu32(data + 5); + if (fp->handle_len > 256) { + /* SFTP doesn't allow handles longer than 256 characters */ + fp->handle_len = 256; + } + fp->handle = (char *)LIBSSH2_ALLOC(session, fp->handle_len); + if (!fp->handle) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for SFTP file/dir handle", 0); + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, fp); + return NULL; + } + memcpy(fp->handle, data + 9, fp->handle_len); + LIBSSH2_FREE(session, data); + + /* Link the file and the sftp session together */ + fp->next = sftp->handles; + if (fp->next) { + fp->next->prev = fp; + } + fp->sftp = sftp; + + fp->u.file.offset = 0; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful"); +#endif + return fp; +} +/* }}} */ + +/* {{{ libssh2_sftp_read + * Read from an SFTP file handle + */ +LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + unsigned long packet_len = handle->handle_len + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + length(4) */ + unsigned char *packet, *s, *data; + unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS }; + size_t bytes_read = 0; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); + return (size_t) -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_READ; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + libssh2_htonu64(s, handle->u.file.offset); s += 8; + libssh2_htonu32(s, buffer_maxlen); s += 4; + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return (size_t)-1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return (size_t)-1; + } + + switch (data[0]) { + case SSH_FXP_STATUS: + sftp->last_errno = libssh2_ntohu32(data + 5); + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + LIBSSH2_FREE(session, data); + return (size_t)-1; + case SSH_FXP_DATA: + bytes_read = libssh2_ntohu32(data + 5); + if (bytes_read > (data_len - 9)) { + return (size_t)-1; + } +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", (unsigned long)bytes_read); +#endif + memcpy(buffer, data + 9, bytes_read); + handle->u.file.offset += bytes_read; + LIBSSH2_FREE(session, data); + return bytes_read; + } + + return (size_t)-1; +} +/* }}} */ + +/* {{{ libssh2_sftp_readdir + * Read from an SFTP directory handle + */ +LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; + unsigned long data_len, request_id, filename_len, num_names; + unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + + if (handle->u.dir.names_left) { + /* A prior request returned more than one directory entry, feed it back from the buffer */ + unsigned char *s = (unsigned char *)handle->u.dir.next_name; + unsigned long real_filename_len = libssh2_ntohu32(s); + + filename_len = real_filename_len; s += 4; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, s, filename_len); s += real_filename_len; + + /* Skip longname */ + s += 4 + libssh2_ntohu32(s); + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + } + s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); + + handle->u.dir.next_name = (char *)s; + if ((--handle->u.dir.names_left) == 0) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } + + return filename_len; + } + + /* Request another entry(entries?) */ + + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_READDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_READDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); +#endif + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_EOF) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } + + num_names = libssh2_ntohu32(data + 5); +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names); +#endif + if (num_names <= 0) { + LIBSSH2_FREE(session, data); + return (num_names == 0) ? 0 : -1; + } + + if (num_names == 1) { + unsigned long real_filename_len = libssh2_ntohu32(data + 9); + + filename_len = real_filename_len; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, data + 13, filename_len); + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + (4 + libssh2_ntohu32(data + 13 + real_filename_len))); + } + LIBSSH2_FREE(session, data); + + return filename_len; + } + + handle->u.dir.names_left = num_names; + handle->u.dir.names_packet = data; + handle->u.dir.next_name = (char *)data + 9; + + /* Be lazy, just use the name popping mechanism from the start of the function */ + return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); +} +/* }}} */ + +/* {{{ libssh2_sftp_write + * Write data to a file handle + */ +LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id, retcode; + unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); + return (size_t)-1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_WRITE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + libssh2_htonu64(s, handle->u.file.offset); s += 8; + libssh2_htonu32(s, count); s += 4; + memcpy(s, buffer, count); s += count; + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_WRITE command", 0); + LIBSSH2_FREE(session, packet); + return (size_t)-1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return (size_t)-1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + handle->u.file.offset += count; + return count; + } + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + + return (size_t)-1; +} +/* }}} */ + +/* {{{ libssh2_sftp_fstat_ex + * Get or Set stat on a file + */ +LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + unsigned long packet_len = handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat"); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + if (setstat) { + s += libssh2_sftp_attr2bin(s, attrs); + } + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, setstat ? (char *)"Unable to send FXP_FSETSTAT" : (char *)"Unable to send FXP_FSTAT command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } + + libssh2_sftp_bin2attr(attrs, data + 5); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_sftp_seek + * Set the read/write pointer to an arbitrary position within the file + */ +LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset) +{ + handle->u.file.offset = offset; +} +/* }}} */ + +/* {{{ libssh2_sftp_tell + * Return the current read/write pointer's offset + */ +LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle) +{ + return handle->u.file.offset; +} +/* }}} */ + + +/* {{{ libssh2_sftp_close_handle + * Close a file or directory handle + * Also frees handle resource and unlinks it from the SFTP structure + */ +LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle"); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_CLOSE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_CLOSE command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode != LIBSSH2_FX_OK) { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + + if (handle == sftp->handles) { + sftp->handles = handle->next; + } + if (handle->next) { + handle->next->prev = NULL; + } + + if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) && + handle->u.dir.names_left) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } + + LIBSSH2_FREE(session, handle->handle); + LIBSSH2_FREE(session, handle); + + return 0; +} +/* }}} */ + +/* ********************** + * SFTP Miscellaneous * + ********************** */ + +/* {{{ libssh2_sftp_unlink_ex + * Delete a file from the remote server + */ +LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, int filename_len) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_REMOVE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, filename_len); s += 4; + memcpy(s, filename, filename_len); s += filename_len; + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } +} +/* }}} */ + +/* {{{ libssh2_sftp_rename_ex + * Rename a file on the remote server + */ +LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filename, int source_filename_len, + char *dest_filename, int dest_filename_len, + long flags) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode = (unsigned long)-1, request_id; + unsigned long packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + + source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ + unsigned char *packet, *s, *data; + + if (sftp->version < 2) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0); + return -1; + } + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", source_filename, dest_filename); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_RENAME; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, source_filename_len); s += 4; + memcpy(s, source_filename, source_filename_len); s += source_filename_len; + libssh2_htonu32(s, dest_filename_len); s += 4; + memcpy(s, dest_filename, dest_filename_len); s += dest_filename_len; + + if (sftp->version >= 5) { + libssh2_htonu32(s, flags); s += 4; + } + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + switch (retcode) { + case LIBSSH2_FX_OK: + retcode = 0; + break; + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "File already exists and SSH_FXP_RENAME_OVERWRITE not specified", 0); + sftp->last_errno = retcode; + retcode = (unsigned long)-1; + break; + case LIBSSH2_FX_OP_UNSUPPORTED: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Operation Not Supported", 0); + sftp->last_errno = retcode; + retcode = (unsigned long)-1; + break; + default: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + retcode = (unsigned long)-1; + } + + return retcode; +} +/* }}} */ + +/* {{{ libssh2_sftp_mkdir_ex + * Create a directory + */ +LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_len, long mode) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs = { LIBSSH2_SFTP_ATTR_PERMISSIONS }; + unsigned long data_len, retcode, request_id; + unsigned long packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR; + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_MKDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + s += libssh2_sftp_attr2bin(s, &attrs); + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + return -1; + } +} +/* }}} */ + +/* {{{ libssh2_sftp_rmdir_ex + * Remove a directory + */ +LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_len) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode, request_id; + unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_RMDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } +} +/* }}} */ + +/* {{{ libssh2_sftp_stat_ex + * Stat a file or symbolic link + */ +LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id; + unsigned long packet_len = path_len + 13 + ((stat_type == LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); +#endif + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + switch (stat_type) { + case LIBSSH2_SFTP_SETSTAT: + *(s++) = SSH_FXP_SETSTAT; + break; + case LIBSSH2_SFTP_LSTAT: + *(s++) = SSH_FXP_LSTAT; + break; + case LIBSSH2_SFTP_STAT: + default: + *(s++) = SSH_FXP_STAT; + } + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + if (stat_type == LIBSSH2_SFTP_SETSTAT) { + s += libssh2_sftp_attr2bin(s, attrs); + } + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send STAT/LSTAT/SETSTAT command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } + + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 5); + LIBSSH2_FREE(session, data); + + return 0; +} +/* }}} */ + +/* {{{ libssh2_sftp_symlink_ex + * Read or set a symlink + */ +LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, int path_len, char *target, int target_len, int link_type) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id, link_len; + unsigned long packet_len = path_len + 13 + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + unsigned char *packet, *s, *data; + unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + + if ((sftp->version < 3) && + (link_type != LIBSSH2_SFTP_REALPATH)) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support SYMLINK or READLINK", 0); + return -1; + } + + s = packet = (unsigned char *)LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0); + return -1; + } + +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", (link_type == LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading", + (link_type == LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path); +#endif + libssh2_htonu32(s, packet_len - 4); s += 4; + switch (link_type) { + case LIBSSH2_SFTP_REALPATH: + *(s++) = SSH_FXP_REALPATH; + break; + case LIBSSH2_SFTP_SYMLINK: + *(s++) = SSH_FXP_SYMLINK; + break; + case LIBSSH2_SFTP_READLINK: + default: + *(s++) = SSH_FXP_READLINK; + } + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, path_len); s += 4; + memcpy(s, path, path_len); s += path_len; + if (link_type == LIBSSH2_SFTP_SYMLINK) { + libssh2_htonu32(s, target_len); s += 4; + memcpy(s, target, target_len); s += target_len; + } + + if (packet_len != (unsigned int)libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SYMLINK/READLINK command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } + + if (libssh2_ntohu32(data + 5) < 1) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Invalid READLINK/REALPATH response, no name entries", 0); + LIBSSH2_FREE(session, data); + return -1; + } + + link_len = libssh2_ntohu32(data + 9); + if (link_len >= (unsigned int)target_len) { + link_len = target_len - 1; + } + memcpy(target, data + 13, link_len); + target[link_len] = 0; + LIBSSH2_FREE(session, data); + + return link_len; +} +/* }}} */ + +/* {{{ libssh2_sftp_last_error + * Returns the last error code reported by SFTP + */ +LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp) +{ + return sftp->last_errno; +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/libssh2/userauth.c b/pandora_agents/win32/ssh/libssh2/userauth.c new file mode 100644 index 0000000000..2359c46203 --- /dev/null +++ b/pandora_agents/win32/ssh/libssh2/userauth.c @@ -0,0 +1,855 @@ +/* Copyright (c) 2004-2005, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + + +/* {{{ proto libssh2_userauth_list + * List authentication methods + * Will yield successful login if "none" happens to be allowable for this user + * Not a common configuration for any SSH server though + * username should be NULL, or a null terminated string + */ +LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, int username_len) +{ + unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; + unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + + method_len(4) + method(4)"none" */ + unsigned long methods_len; + unsigned char *data, *s; + + s = data = (unsigned char *) LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth_list", 0); + return NULL; + } + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + if (username) { + memcpy(s, username, username_len); s += username_len; + } + + libssh2_htonu32(s, 14); s += 4; + memcpy(s, "ssh-connection", 14); s += 14; + + libssh2_htonu32(s, 4); s += 4; + memcpy(s, "none", 4); s += 4; + + if (libssh2_packet_write(session, data, data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request", 0); + LIBSSH2_FREE(session, data); + return NULL; + } + LIBSSH2_FREE(session, data); + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return NULL; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { + /* Wow, who'dve thought... */ + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return NULL; + } + + methods_len = libssh2_ntohu32(data + 1); + memcpy(data, data + 5, methods_len); + data[methods_len] = '\0'; +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", data); +#endif + return (char *) data; +} +/* }}} */ + +/* {{{ libssh2_userauth_authenticated + * 0 if not yet authenticated + * non-zero is already authenticated + */ +LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session) +{ + return session->state & LIBSSH2_STATE_AUTHENTICATED; +} +/* }}} */ + +/* {{{ libssh2_userauth_password + * Plain ol' login + */ +LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + const char *password, int password_len, + LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) +{ + unsigned char *data, *s, reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 }; + unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" + + method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */ + + s = data = (unsigned char *) LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password request", 0); + return -1; + } + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + + libssh2_htonu32(s, sizeof("password") - 1); s += 4; + memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; + + *s = '\0'; s++; + + libssh2_htonu32(s, password_len); s += 4; + memcpy(s, password, password_len); s += password_len; + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication"); +#endif + if (libssh2_packet_write(session, data, data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0); + LIBSSH2_FREE(session, data); + return -1; + } + LIBSSH2_FREE(session, data); + + password_response: + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return -1; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful"); +#endif + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + if (data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) { + char *newpw = NULL; + int newpw_len = 0; + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required"); +#endif + LIBSSH2_FREE(session, data); + if (passwd_change_cb) { + passwd_change_cb(session, &newpw, &newpw_len, &session->abstract); + if (!newpw) { + libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0); + return -1; + } + data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */ + s = data = (unsigned char *) LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0); + return -1; + } + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + + libssh2_htonu32(s, sizeof("password") - 1); s += 4; + memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1; + + *s = 0xFF; s++; + + libssh2_htonu32(s, password_len); s += 4; + memcpy(s, password, password_len); s += password_len; + + libssh2_htonu32(s, newpw_len); s += 4; + memcpy(s, newpw, newpw_len); s += newpw_len; + + if (libssh2_packet_write(session, data, data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0); + LIBSSH2_FREE(session, data); + return -1; + } + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, newpw); + + /* Ugliest use of goto ever. Blame it on the askN => requirev migration. */ + goto password_response; + } else { + libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0); + return -1; + } + } + + /* FAILURE */ + LIBSSH2_FREE(session, data); + return -1; +} +/* }}} */ + +/* {{{ libssh2_file_read_publickey + * Read a public key from an id_???.pub style file + */ +static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char **method, unsigned long *method_len, + unsigned char **pubkeydata, unsigned long *pubkeydata_len, + const char *pubkeyfile) +{ + FILE *fd; + char *pubkey = NULL, c, *sp1, *sp2, *tmp; + int pubkey_len = 0, tmp_len; + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading public key file: %s", pubkeyfile); +#endif + /* Read Public Key */ + fd = fopen(pubkeyfile, "r"); + if (!fd) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to open public key file", 0); + return -1; + } + while (!feof(fd) && (c = fgetc(fd)) != '\r' && c != '\n') pubkey_len++; + rewind(fd); + + if (pubkey_len <= 1) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid data in public key file", 0); + fclose(fd); + return -1; + } + + pubkey = (char *) LIBSSH2_ALLOC(session, pubkey_len); + if (!pubkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for public key data", 0); + fclose(fd); + return -1; + } + if (fread(pubkey, 1, pubkey_len, fd) != (unsigned int) pubkey_len) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to read public key from file", 0); + LIBSSH2_FREE(session, pubkey); + fclose(fd); + return -1; + } + fclose(fd); + while (pubkey_len && (pubkey[pubkey_len-1] == '\r' || pubkey[pubkey_len-1] == '\n')) pubkey_len--; + + if (!pubkey_len) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Missing public key data", 0); + LIBSSH2_FREE(session, pubkey); + return -1; + } + + if ((sp1 = (char *) memchr(pubkey, ' ', pubkey_len)) == NULL) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid public key data", 0); + LIBSSH2_FREE(session, pubkey); + return -1; + } + /* Wasting some bytes here (okay, more than some), + * but since it's likely to be freed soon anyway, + * we'll just avoid the extra free/alloc and call it a wash */ + *method = (unsigned char *) pubkey; + *method_len = sp1 - pubkey; + + sp1++; + + if ((sp2 = (char *) memchr(sp1, ' ', pubkey_len - *method_len)) == NULL) { + /* Assume that the id string is missing, but that it's okay */ + sp2 = pubkey + pubkey_len; + } + + if (libssh2_base64_decode(session, &tmp, &tmp_len, sp1, sp2 - sp1)) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid key data, not base64 encoded", 0); + LIBSSH2_FREE(session, pubkey); + return -1; + } + *pubkeydata = (unsigned char *) tmp; + *pubkeydata_len = tmp_len; + + return 0; +} +/* }}} */ + +/* {{{ libssh2_file_read_privatekey + * Read a PEM encoded private key from an id_??? style file + */ +static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, LIBSSH2_HOSTKEY_METHOD **hostkey_method, void **hostkey_abstract, + const char *method, int method_len, + const char *privkeyfile, const char *passphrase) +{ + LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = libssh2_hostkey_methods(); + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading private key file: %s", privkeyfile); +#endif + *hostkey_method = NULL; + *hostkey_abstract = NULL; + while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) { + if ((*hostkey_methods_avail)->initPEM && + strncmp((*hostkey_methods_avail)->name, method, method_len) == 0) { + *hostkey_method = *hostkey_methods_avail; + break; + } + hostkey_methods_avail++; + } + if (!*hostkey_method) { + libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, "No handler for specified private key", 0); + return -1; + } + + if ((*hostkey_method)->initPEM(session, (unsigned char *) privkeyfile, (unsigned char *) passphrase, hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to initialize private key from file", 0); + return -1; + } + + return 0; +} +/* }}} */ + +/* {{{ libssh2_userauth_hostbased_fromfile_ex + * Authenticate using a keypair found in the named files + */ +LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + const char *publickey, const char *privatekey, + const char *passphrase, + const char *hostname, int hostname_len, + const char *local_username, int local_username_len) +{ + LIBSSH2_HOSTKEY_METHOD *privkeyobj; + void *abstract; + unsigned char buf[5]; + struct iovec datavec[4]; + unsigned char *method, *pubkeydata, *packet, *s, *sig, *data, reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; + unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; + + if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } + + packet_len = username_len + method_len + hostname_len + local_username_len + pubkeydata_len + 48; + /* packet_type(1) + username_len(4) + servicename_len(4) + service_name(14)"ssh-connection" + + * authmethod_len(4) + authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + + * local_username_len(4) + */ + /* Preallocate space for an overall length, method name again, + * and the signature, which won't be any larger than the size of the publickeydata itself */ + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, 14); s += 4; + memcpy(s, "ssh-connection", 14); s += 14; + + libssh2_htonu32(s, 9); s += 4; + memcpy(s, "hostbased", 9); s += 9; + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + + libssh2_htonu32(s, pubkeydata_len); s += 4; + memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; + + libssh2_htonu32(s, hostname_len); s += 4; + memcpy(s, hostname, hostname_len); s += hostname_len; + + libssh2_htonu32(s, local_username_len); s += 4; + memcpy(s, local_username, local_username_len); s += local_username_len; + + if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, (char *) method, method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + return -1; + } + + libssh2_htonu32(buf, session->session_id_len); + datavec[0].iov_base = (char *) buf; + datavec[0].iov_len = 4; + datavec[1].iov_base = (char *) session->session_id; + datavec[1].iov_len = session->session_id_len; + datavec[2].iov_base = (char *) packet; + datavec[2].iov_len = packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + + if (sig_len > pubkeydata_len ) { + /* Should *NEVER* happen, but...well.. better safe than sorry */ + packet = (unsigned char *) LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-hostbased packet", 0); + LIBSSH2_FREE(session, method); + return -1; + } + } + + s = packet + packet_len; + + libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + LIBSSH2_FREE(session, method); + + libssh2_htonu32(s, sig_len); s += 4; + memcpy(s, sig, sig_len); s += sig_len; + LIBSSH2_FREE(session, sig); + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting hostbased authentication"); +#endif + if (libssh2_packet_write(session, packet, s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return -1; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Hostbased authentication successful"); +#endif + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); + return -1; +} +/* }}} */ + +/* {{{ libssh2_userauth_publickey_fromfile_ex + * Authenticate using a keypair found in the named files + */ +LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + const char *publickey, const char *privatekey, + const char *passphrase) +{ + LIBSSH2_HOSTKEY_METHOD *privkeyobj; + void *abstract; + unsigned char buf[5]; + struct iovec datavec[4]; + unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data; + unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 }; + unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len; + + if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) { + return -1; + } + + packet_len = username_len + method_len + pubkeydata_len + 45; /* packet_type(1) + username_len(4) + servicename_len(4) + + service_name(14)"ssh-connection" + authmethod_len(4) + + authmethod(9)"publickey" + sig_included(1)'\0' + + algmethod_len(4) + publickey_len(4) */ + /* Preallocate space for an overall length, method name again, + * and the signature, which won't be any larger than the size of the publickeydata itself */ + s = packet = (unsigned char *) LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len)); + + *(s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + libssh2_htonu32(s, 14); s += 4; + memcpy(s, "ssh-connection", 14); s += 14; + + libssh2_htonu32(s, 9); s += 4; + memcpy(s, "publickey", 9); s += 9; + + b = s; + *(s++) = 0; /* Not sending signature with *this* packet */ + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + + libssh2_htonu32(s, pubkeydata_len); s += 4; + memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len; + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication"); +#endif + if (libssh2_packet_write(session, packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, pubkeydata); + return -1; + } + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, pubkeydata); + return -1; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful"); +#endif + /* God help any SSH server that allows an UNVERIFIED public key to validate the user */ + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, pubkeydata); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + if (data[0] == SSH_MSG_USERAUTH_FAILURE) { + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, packet); + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, pubkeydata); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0); + return -1; + } + + /* Semi-Success! */ + LIBSSH2_FREE(session, data); + LIBSSH2_FREE(session, pubkeydata); + + if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, (char *) method, method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + return -1; + } + + *b = 0xFF; + + libssh2_htonu32(buf, session->session_id_len); + datavec[0].iov_base = (char *) buf; + datavec[0].iov_len = 4; + datavec[1].iov_base = (char *) session->session_id; + datavec[1].iov_len = session->session_id_len; + datavec[2].iov_base = (char *) packet; + datavec[2].iov_len = packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, method); + LIBSSH2_FREE(session, packet); + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + + if (sig_len > pubkeydata_len) { + /* Should *NEVER* happen, but...well.. better safe than sorry */ + packet = (unsigned char *) LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */ + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0); + LIBSSH2_FREE(session, method); + return -1; + } + } + + s = packet + packet_len; + + libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4; + + libssh2_htonu32(s, method_len); s += 4; + memcpy(s, method, method_len); s += method_len; + LIBSSH2_FREE(session, method); + + libssh2_htonu32(s, sig_len); s += 4; + memcpy(s, sig, sig_len); s += sig_len; + LIBSSH2_FREE(session, sig); + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2"); +#endif + if (libssh2_packet_write(session, packet, s - packet)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + LIBSSH2_FREE(session, packet); + + /* PK_OK is no longer valid */ + reply_codes[2] = 0; + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return -1; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful"); +#endif + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0); + return -1; +} +/* }}} */ + +/* {{{ libssh2_userauth_keyboard_interactive + * Authenticate using a challenge-response authentication + */ +LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, const char *username, int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) +{ + unsigned char *s, *data; /* packet */ + unsigned long packet_len; + + packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */ + + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ + + 4 + 14 /* string service name (US-ASCII) */ + + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ + + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ + + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ + ; + + if (!(data = s = (unsigned char *) LIBSSH2_ALLOC(session, packet_len))) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive authentication", 0); + return -1; + } + + *s++ = SSH_MSG_USERAUTH_REQUEST; + + /* user name */ + libssh2_htonu32(s, username_len); s += 4; + memcpy(s, username, username_len); s += username_len; + + /* service name */ + libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1; + + /* "keyboard-interactive" */ + libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); s += 4; + memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); s += sizeof("keyboard-interactive") - 1; + + /* language tag */ + libssh2_htonu32(s, 0); s += 4; + + /* submethods */ + libssh2_htonu32(s, 0); s += 4; + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting keyboard-interactive authentication"); +#endif + if (libssh2_packet_write(session, data, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send keyboard-interactive request", 0); + LIBSSH2_FREE(session, data); + return -1; + } + LIBSSH2_FREE(session, data); + + for (;;) { + unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 }; + unsigned int auth_name_len; + char* auth_name = NULL; + unsigned auth_instruction_len; + char* auth_instruction = NULL; + unsigned int language_tag_len; + unsigned long data_len; + unsigned int num_prompts = 0; + unsigned int i; + int auth_failure = 1; + LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts = NULL; + LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses = NULL; + + if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) { + return -1; + } + + if (data[0] == SSH_MSG_USERAUTH_SUCCESS) { +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive authentication successful"); +#endif + LIBSSH2_FREE(session, data); + session->state |= LIBSSH2_STATE_AUTHENTICATED; + return 0; + } + + if (data[0] == SSH_MSG_USERAUTH_FAILURE) { + LIBSSH2_FREE(session, data); + return -1; + } + + /* server requested PAM-like conversation */ + + s = data + 1; + + /* string name (ISO-10646 UTF-8) */ + auth_name_len = libssh2_ntohu32(s); s += 4; + if (!(auth_name = (char *) LIBSSH2_ALLOC(session, auth_name_len))) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'name' request field", 0); + goto cleanup; + } + memcpy(auth_name, s, auth_name_len); s += auth_name_len; + + /* string instruction (ISO-10646 UTF-8) */ + auth_instruction_len = libssh2_ntohu32(s); s += 4; + if (!(auth_instruction = (char *) LIBSSH2_ALLOC(session, auth_instruction_len))) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'instruction' request field", 0); + goto cleanup; + } + memcpy(auth_instruction, s, auth_instruction_len); s += auth_instruction_len; + + /* string language tag (as defined in [RFC-3066]) */ + language_tag_len = libssh2_ntohu32(s); s += 4; + /* ignoring this field as deprecated */ s += language_tag_len; + + /* int num-prompts */ + num_prompts = libssh2_ntohu32(s); s += 4; + + prompts = (LIBSSH2_USERAUTH_KBDINT_PROMPT *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); + if (!prompts) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompts array", 0); + goto cleanup; + } + memset(prompts, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts); + + responses = (LIBSSH2_USERAUTH_KBDINT_RESPONSE *) LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); + if (!responses) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive responses array", 0); + goto cleanup; + } + memset(responses, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts); + + for(i = 0; i != num_prompts; ++i) { + /* string prompt[1] (ISO-10646 UTF-8) */ + prompts[i].length = libssh2_ntohu32(s); s += 4; + if (!(prompts[i].text = (char *) LIBSSH2_ALLOC(session, prompts[i].length))) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompt message", 0); + goto cleanup; + } + memcpy(prompts[i].text, s, prompts[i].length); s += prompts[i].length; + + /* boolean echo[1] */ + prompts[i].echo = *s++; + } + + response_callback(auth_name, auth_name_len, auth_instruction, auth_instruction_len, num_prompts, prompts, responses, &session->abstract); + +#ifdef LIBSSH2_DEBUG_USERAUTH + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive response callback function invoked"); +#endif + + packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + + 4 /* int num-responses */ + ; + + for (i = 0; i != num_prompts; ++i) { + packet_len += 4 + responses[i].length; /* string response[1] (ISO-10646 UTF-8) */ + } + + if (!(data = s = (unsigned char *) LIBSSH2_ALLOC(session, packet_len))) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive response packet", 0); + goto cleanup; + } + + *s = SSH_MSG_USERAUTH_INFO_RESPONSE; s++; + libssh2_htonu32(s, num_prompts); s += 4; + + for (i = 0; i != num_prompts; ++i) { + libssh2_htonu32(s, responses[i].length); s += 4; + memcpy(s, responses[i].text, responses[i].length); s += responses[i].length; + } + + if (libssh2_packet_write(session, data, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-keyboard-interactive request", 0); + goto cleanup; + } + + auth_failure = 0; + + cleanup: + /* It's safe to clean all the data here, because unallocated pointers + * are filled by zeroes + */ + + LIBSSH2_FREE(session, data); + + if (prompts) { + for (i = 0; i != num_prompts; ++i) { + LIBSSH2_FREE(session, prompts[i].text); + } + } + + if (responses) { + for (i = 0; i != num_prompts; ++i) { + LIBSSH2_FREE(session, responses[i].text); + } + } + + LIBSSH2_FREE(session, prompts); + LIBSSH2_FREE(session, responses); + + if (auth_failure) { + return -1; + } + } +} +/* }}} */ diff --git a/pandora_agents/win32/ssh/pandora_ssh_client.cc b/pandora_agents/win32/ssh/pandora_ssh_client.cc new file mode 100644 index 0000000000..ab767ab617 --- /dev/null +++ b/pandora_agents/win32/ssh/pandora_ssh_client.cc @@ -0,0 +1,249 @@ +/* Class to abstract an SSH client. It uses libssh2. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_ssh_client.h" +#include "../misc/pandora_file.h" +#include "../pandora_strutils.h" +#include "libssh2/libssh2_sftp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace SSH; +using namespace Pandora; + +Connection_Failed::Connection_Failed (int e) { + err_number = e; +} + +int +Connection_Failed::getError () { + return err_number; +} +Scp_Failed::Scp_Failed (char *e) { + errmsg = strdup (e); +} + +Pandora_Ssh_Client::Pandora_Ssh_Client () { + sock = 0; + fingerprint = ""; + session = NULL; + channel = NULL; + return; +} + +Pandora_Ssh_Client::~Pandora_Ssh_Client () { + if (session != NULL) { + disconnect (); + } + + return; +} + +/* Disconnects from remote host. It will close all open connections and channels. */ +void +Pandora_Ssh_Client::disconnect () { + if (channel != NULL) { + libssh2_channel_send_eof (channel); + libssh2_channel_close (channel); + libssh2_channel_wait_closed (channel); + libssh2_channel_free (channel); + channel = NULL; + } + + if (session != NULL) { + libssh2_session_disconnect (session, ""); + libssh2_session_free (session); + session = NULL; + } + + if (sock != 0) { + closesocket (sock); + sock = 0; + } +} + +void +Pandora_Ssh_Client::newConnection (const string host, const int port) { + struct sockaddr_in sin; + struct hostent *resolv = NULL; + WSADATA wsadata; + string finger_aux; + char char_aux[3]; + + if (session != NULL) { + throw Session_Already_Opened (); + } + + WSAStartup (2, &wsadata); + + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + throw Socket_Error (); + } + + resolv = (struct hostent *) gethostbyname (host.c_str ()); + + if (resolv == NULL) { + disconnect (); + throw Resolv_Failed (); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + sin.sin_addr = *((struct in_addr *)resolv->h_addr); + + if (connect (sock, (struct sockaddr*) (&sin), + sizeof (struct sockaddr_in)) == -1) { + disconnect (); + throw Connection_Failed (WSAGetLastError ()); + } + + session = libssh2_session_init(); + if (libssh2_session_startup (session, sock) != 0) { + disconnect (); + throw Session_Error (); + } + + /* Get the fingerprint and transform it to a hexadecimal readable + string */ + finger_aux = libssh2_hostkey_hash (session, + LIBSSH2_HOSTKEY_HASH_MD5); + fingerprint = ""; + for (int i = 0; i < 16; i++) { + sprintf (char_aux, "%02X:", (unsigned char) finger_aux[i]); + fingerprint += (char *) char_aux; + } + + fingerprint.erase (fingerprint.length () - 1, 2); +} + +/* Connects to specified host and port using a username and a password */ +void +Pandora_Ssh_Client::connectWithUserPass (const string host, const int port, + const string username, const string passwd) { + try { + newConnection (host, port); + } catch (Session_Already_Opened e) { + } + + if (session != NULL) { + if (libssh2_userauth_password (session, username.c_str (), + passwd.c_str ())) { + disconnect (); + throw Authentication_Failed (); + } + } + return; +} + +/* Connects to specified host and port using a username and a public/private key. + * The keys are the filename that contains the public and the private keys. + * The passphrase is the password for these keys. */ +void +Pandora_Ssh_Client::connectWithPublicKey (const string host, const int port, + const string username, const string filename_pubkey, + const string filename_privkey, const string passphrase) { + try { + newConnection (host, port); + } catch (Session_Already_Opened e) { + } + + if (session != NULL) { + if (libssh2_userauth_publickey_fromfile (session, + username.c_str (), + filename_pubkey.c_str (), + filename_privkey.c_str (), + passphrase.c_str ())) { + disconnect (); + throw Authentication_Failed (); + } + } + return; +} + +/* Copy a file using a SSH connection (scp). + * The function receives a filename in the local filesystem and copies all + * its content to the remote host. The remote filename will be the + * basename of the local file and will be copied in the remote actual + * directory. */ +void +Pandora_Ssh_Client::scpFileFilename (const string remote_filename, const string filename) { + LIBSSH2_CHANNEL *scp_channel; + size_t to_send, sent; + char *errmsg; + int errmsg_len; + string buffer; + + if (session == NULL) { + throw Session_Not_Opened (); + } + try { + buffer = Pandora_File::readFile (filename); + } catch (Pandora_File::File_Not_Found e) { + pandoraLog ("Pandora_Ssh_Client: File %s not found", + filename.c_str()); + return; + } + + to_send = buffer.length (); + + scp_channel = libssh2_scp_send (session, remote_filename.c_str (), 0666, + to_send); + if (scp_channel == NULL) { + throw Channel_Error (); + } + + libssh2_channel_set_blocking (scp_channel, 1); + + /* FIXME: It may crash if the scp fails, maybe because of a libssh2 bug */ + sent = libssh2_channel_write (scp_channel, buffer.c_str (), to_send); + + if (sent < 0) { + Scp_Failed *e; + errmsg = (char *) malloc (sizeof (char) * 1000); + libssh2_session_last_error (session, &errmsg, &errmsg_len, 1); + pandoraDebug ("Error %d on SCP %s", sent, errmsg); + e = new Scp_Failed (errmsg); + + libssh2_channel_close (scp_channel); + libssh2_channel_wait_closed (scp_channel); + libssh2_channel_free (scp_channel); + Pandora::pandoraFree (errmsg); + throw *e; + } + libssh2_channel_send_eof (scp_channel); + + libssh2_channel_close (scp_channel); + libssh2_channel_wait_closed (scp_channel); + libssh2_channel_free (scp_channel); +} + +string +Pandora_Ssh_Client::getFingerprint () { + return this->fingerprint; +} diff --git a/pandora_agents/win32/ssh/pandora_ssh_client.h b/pandora_agents/win32/ssh/pandora_ssh_client.h new file mode 100644 index 0000000000..9e2ae1e724 --- /dev/null +++ b/pandora_agents/win32/ssh/pandora_ssh_client.h @@ -0,0 +1,112 @@ +/* Class to abstract an SSH client. It uses libssh2. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_SSH_CLIENT_H__ +#define __PANDORA_SSH_CLIENT_H__ + +#include +#include "../pandora.h" +#include "libssh2/libssh2.h" + +using namespace std; + +namespace SSH { + /* SSH Client exceptions */ + class Session_Already_Opened : public Pandora::Pandora_Exception { + }; + + class Session_Not_Opened : public Pandora::Pandora_Exception { + }; + + class Session_Error : public Pandora::Pandora_Exception { + }; + + class Authentication_Failed : public Pandora::Pandora_Exception { + }; + + class Resolv_Failed : public Pandora::Pandora_Exception { + }; + + class Socket_Error : public Pandora::Pandora_Exception { + }; + + class File_Error : public Pandora::Pandora_Exception { + }; + + class Channel_Error : public Pandora::Pandora_Exception { + }; + + class Connection_Failed : public Pandora::Pandora_Exception { + private: + int err_number; + public: + Connection_Failed (int e); + int getError (); + }; + + class Scp_Failed : public Pandora::Pandora_Exception { + private: + char *errmsg; + public: + Scp_Failed (char *e); + ~Scp_Failed () { Pandora::pandoraFree (errmsg); }; + }; + + /* SSH Client class */ + class Pandora_Ssh_Client { + private: + int sock; + string fingerprint; + LIBSSH2_SESSION *session; + LIBSSH2_CHANNEL *channel; + + void newConnection (const string host, const int port); + public: + Pandora_Ssh_Client (); + ~Pandora_Ssh_Client (); + + /* Connects to specified host and port using a username and a password + * + * Throws: Authentication_Failed */ + void connectWithUserPass (const string host, const int port, + const string username, const string passwd); + /* Connects to specified host and port using a username and a public/private key. + * The keys are the filename that contains the public and the private keys. + * The passphrase is the password for these keys. + * + * Throws: Authentication_Failed */ + void connectWithPublicKey (const string host, const int port, + const string username, const string filename_pubkey, + const string filename_privkey, const string passphrase); + + /* Disconnects from remote host. It will close all open connections and channels. */ + void disconnect (); + + /* Copy a file using a SSH connection (scp). + * The function receives a filename in the local filesystem and copies all + * its content to the remote host. The remote filename will be the + * first argument. */ + void scpFileFilename (const string remote_filename, + const string filename); + + string getFingerprint (); + }; +} +#endif diff --git a/pandora_agents/win32/ssh/pandora_ssh_test.cc b/pandora_agents/win32/ssh/pandora_ssh_test.cc new file mode 100644 index 0000000000..4926ebdb18 --- /dev/null +++ b/pandora_agents/win32/ssh/pandora_ssh_test.cc @@ -0,0 +1,157 @@ +/* Test module to prove SSH connection. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_ssh_test.h" +#include "../pandora_agent_conf.h" +#include "../tinyxml/tinyxml.h" +#include "../misc/pandora_file.h" +#include + +using namespace std; +using namespace SSH; + +Pandora_SSH_Test::Pandora_SSH_Test () { + string conf_file; + + conf_file = Pandora::getPandoraInstallDir (); + conf_file += "pandora_agent.conf"; + conf = new Pandora_Agent_Conf (conf_file); + + ssh_client = new SSH::Pandora_Ssh_Client (); +} + +Pandora_SSH_Test::~Pandora_SSH_Test () { + delete conf; + delete ssh_client; +} + +void +Pandora_SSH_Test::test () { + string pubkey_file, privkey_file, tmp_filename; + string remote_host, remote_filepath, tmp_filepath; + TiXmlDocument *doc; + TiXmlDeclaration *decl; + bool saved; + + remote_host = this->conf->getValue ("server_ip"); + + pubkey_file = Pandora::getPandoraInstallDir (); + pubkey_file += "key\\id_dsa.pub"; + privkey_file = Pandora::getPandoraInstallDir (); + privkey_file += "key\\id_dsa"; + + cout << "Public key file: " << pubkey_file << endl; + cout << "Private key file: " << privkey_file << endl; + cout << "Connecting with " << remote_host << "..." << endl; + + try { + this->ssh_client->connectWithPublicKey (remote_host.c_str (), 22, + "pandora", pubkey_file, + privkey_file, ""); + } catch (Authentication_Failed e) { + cout << "Authentication Failed when connecting to " + << remote_host << endl; + cout << "Check the remote host configuration and the public/private key files." + << endl; + throw e; + } catch (Socket_Error e) { + cout << "Socket error when connecting to " + << remote_host << endl; + cout << "Check the network configuration." << endl; + throw e; + } catch (Resolv_Failed e) { + cout << "Could not resolv " + << remote_host << endl; + cout << "Check the network configuration." << endl; + throw e; + } catch (Connection_Failed e) { + cout << "Connection error number " << e.getError () << endl; + cout << "Check the network configuration." << endl; + throw e; + } catch (Session_Error e) { + cout << "Error while opening SSH session." << endl; + cout << "Check the network configuration." << endl; + throw e; + } + + cout << "Authentication successful." << endl; + cout << "Host fingerprint: " << this->ssh_client->getFingerprint () + << endl; + + tmp_filename = "ssh.test"; + tmp_filepath = conf->getValue ("temporal"); + if (tmp_filepath[tmp_filepath.length () - 1] != '\\') { + tmp_filepath += "\\"; + } + tmp_filepath += tmp_filename; + + decl = new TiXmlDeclaration( "1.0", "ISO-8859-1", "" ); + doc = new TiXmlDocument (tmp_filepath); + doc->InsertEndChild (*decl); + saved = doc->SaveFile(); + delete doc; + if (!saved) { + Pandora_Exception e; + cout << "Error when saving the XML in " << tmp_filepath << endl; + cout << "Check the configuration file" << endl; + throw e; + } + + cout << "Created a blank XML file in " << tmp_filepath<< endl; + + remote_filepath = conf->getValue ("server_path"); + cout << "Remote copying " << tmp_filepath << "on server " << remote_host + << " at " << remote_filepath << tmp_filename << endl; + try { + ssh_client->scpFileFilename (remote_filepath + tmp_filename, + tmp_filepath); + } catch (Session_Not_Opened e) { + ssh_client->disconnect(); + cout << "The SSH session could not be created." << endl; + cout << "Check the network configuration." << endl; + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_Exception e) { + } + throw e; + } catch (Scp_Failed e) { + ssh_client->disconnect(); + cout << "The copying operation could not finished." << endl; + cout << "Check the network configuration." << endl; + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_Exception e) { + } + throw e; + } catch (Pandora_Exception e) { + ssh_client->disconnect(); + cout << "An unhandled exception happened." << endl; + throw e; + } + + cout << "Successfuly file copied to remote host " << endl; + ssh_client->disconnect(); + cout << "Successfuly disconnected from remote host " << endl; + try { + Pandora_File::removeFile (tmp_filepath); + } catch (Pandora_File::Delete_Error e) { + } + cout << "The SSH test was successful!" << endl; +} diff --git a/pandora_agents/win32/ssh/pandora_ssh_test.h b/pandora_agents/win32/ssh/pandora_ssh_test.h new file mode 100644 index 0000000000..46ec907e58 --- /dev/null +++ b/pandora_agents/win32/ssh/pandora_ssh_test.h @@ -0,0 +1,34 @@ +/* Test module to prove SSH connection. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_ssh_client.h" +#include "../pandora_agent_conf.h" + +namespace SSH { + class Pandora_SSH_Test { + private: + Pandora_Ssh_Client *ssh_client; + Pandora_Agent_Conf *conf; + public: + Pandora_SSH_Test (); + ~Pandora_SSH_Test (); + void test (); + }; +} diff --git a/pandora_agents/win32/windows/pandora_windows_info.cc b/pandora_agents/win32/windows/pandora_windows_info.cc new file mode 100644 index 0000000000..64d6fb2ad7 --- /dev/null +++ b/pandora_agents/win32/windows/pandora_windows_info.cc @@ -0,0 +1,58 @@ +/* Functions to get information about Windows. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "pandora_windows_info.h" +#include "../pandora_strutils.h" + +#define MAX_KEY_LENGTH 255 + +using namespace Pandora_Strutils; +using namespace Pandora_Windows_Info; + +string +Pandora_Windows_Info::getOSName () { + return Pandora_Wmi::getOSName (); +} + +string +Pandora_Windows_Info::getOSVersion () { + return Pandora_Wmi::getOSVersion (); +} + +string +Pandora_Windows_Info::getOSBuild () { + return Pandora_Wmi::getOSBuild(); +} + +string +Pandora_Windows_Info::getSystemName () { + return Pandora_Wmi::getSystemName (); +} + +string +Pandora_Windows_Info::getSystemPath () { + char buffer[MAX_PATH]; + + ::GetWindowsDirectory (buffer, MAX_PATH+1); + + string str_path = buffer; + str_path = trim (str_path); + return str_path; +} diff --git a/pandora_agents/win32/windows/pandora_windows_info.h b/pandora_agents/win32/windows/pandora_windows_info.h new file mode 100644 index 0000000000..169effe31f --- /dev/null +++ b/pandora_agents/win32/windows/pandora_windows_info.h @@ -0,0 +1,41 @@ +/* Functions to get information about Windows operating system. + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_WINDOWS_INFO_H__ +#define __PANDORA_WINDOWS_INFO_H__ + +#include +#include "../pandora.h" +#include +#include +#include "pandora_wmi.h" + +using namespace Pandora; +using namespace std; + +namespace Pandora_Windows_Info { + + string getOSName (); + string getOSVersion (); + string getOSBuild (); + string getSystemName (); + string getSystemPath (); +} +#endif diff --git a/pandora_agents/win32/windows/pandora_wmi.cc b/pandora_agents/win32/windows/pandora_wmi.cc new file mode 100644 index 0000000000..bf0a3d6c36 --- /dev/null +++ b/pandora_agents/win32/windows/pandora_wmi.cc @@ -0,0 +1,267 @@ +/* Class to manage the Windows Management Instrumentation(WMI). + It depends on disphelper library (http://disphelper.sourceforge.net) + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MAB02111-1307, USA. +*/ + +#include "pandora_wmi.h" +#include "../pandora_strutils.h" +#include +#include +#include + +using namespace std; +using namespace Pandora_Wmi; + +static LPWSTR +getWmiStr (LPCWSTR computer) { + static WCHAR wmi_str [256]; + + wcscpy (wmi_str, L"winmgmts:{impersonationLevel=impersonate}!\\\\"); + + if (computer) { + wcsncat (wmi_str, computer, 128); + } else { + wcscat (wmi_str, L"."); + } + + wcscat (wmi_str, L"\\root\\cimv2"); + + return wmi_str; +} + +int +Pandora_Wmi::isProcessRunning (string process_name) { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string name; + int result = 0; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, description, state; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_Process")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".Name"); + + name = fix.name; + transform (name.begin (), name.end (), name.begin (), + (int (*) (int)) tolower); + pandoraDebug ("name %s", name.c_str ()); + + if (process_name == name) { + result++; + } + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return result; +} + +int +Pandora_Wmi::isServiceRunning (string service_name) { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string name, state; + int result = 0; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, state; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_Service")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".Name"); + + name = fix.name; + transform (name.begin (), name.end (), name.begin (), + (int (*) (int)) tolower); + pandoraDebug ("name %s", name.c_str ()); + + if (service_name == name) { + dhGetValue (L"%s", &fix.state, quickfix, + L".State"); + state = fix.state; + pandoraDebug ("state %s", state.c_str ()); + } + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return result; +} + +string +Pandora_Wmi::getOSName () { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string ret; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, state, description; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_OperatingSystem ")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".Caption"); + + ret = fix.name; + + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return ret; +} + +string +Pandora_Wmi::getOSVersion () { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string ret; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, state, description; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_OperatingSystem ")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".CSDVersion"); + + ret = fix.name; + + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return ret; +} + +string +Pandora_Wmi::getOSBuild () { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string ret; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, state, description; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_OperatingSystem ")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".Version"); + + ret = fix.name; + + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return ret; +} + +string +Pandora_Wmi::getSystemName () { + CDhInitialize init; + CDispPtr wmi_svc, quickfixes; + string ret; + + dhToggleExceptions (TRUE); + + struct QFix { + CDhStringA name, state, description; + }; + + try { + dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc)); + dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc, + L".ExecQuery(%S)", + L"SELECT * FROM Win32_OperatingSystem ")); + + FOR_EACH (quickfix, quickfixes, NULL) { + QFix fix = { 0 }; + + dhGetValue (L"%s", &fix.name, quickfix, + L".CSName"); + + ret = fix.name; + + } NEXT_THROW (quickfix); + } catch (string errstr) { + cerr << "Fatal error details:" << endl << errstr << endl; + } + + return ret; +} diff --git a/pandora_agents/win32/windows/pandora_wmi.h b/pandora_agents/win32/windows/pandora_wmi.h new file mode 100644 index 0000000000..b3dd7de218 --- /dev/null +++ b/pandora_agents/win32/windows/pandora_wmi.h @@ -0,0 +1,41 @@ +/* Class to manage the Windows Management Instrumentation(WMI). + It depends on disphelper library (http://disphelper.sourceforge.net) + + Copyright (C) 2006 Artica ST. + Written by Esteban Sanchez. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __PANDORA_WMI_H__ +#define __PANDORA_WMI_H__ + +#include "../pandora.h" +#include "wmi/disphelper.h" +#include + +using namespace Pandora; +using namespace std; + +namespace Pandora_Wmi { + int isProcessRunning (string process_name); + int isServiceRunning (string service_name); + string getOSName (); + string getOSVersion (); + string getOSBuild (); + string getSystemName (); +}; + +#endif diff --git a/pandora_agents/win32/windows/wmi/disphelper.c b/pandora_agents/win32/windows/wmi/disphelper.c new file mode 100644 index 0000000000..815a441920 --- /dev/null +++ b/pandora_agents/win32/windows/wmi/disphelper.c @@ -0,0 +1,1292 @@ +/* This file is the compacted single file version of the DispHelper COM helper library. + * DispHelper allows you to call COM objects with an extremely simple printf style syntax. + * DispHelper can be used from C++ or even plain C. It works with most Windows compilers + * including Dev-CPP, Visual C++ and LCC-WIN32. Including DispHelper in your project + * couldn't be simpler as it is available in a compacted single file version (this file). + * + * Included with DispHelper are over 20 samples that demonstrate using COM objects + * including ADO, CDO, Outlook, Eudora, Excel, Word, Internet Explorer, MSHTML, + * PocketSoap, Word Perfect, MS Agent, SAPI, MSXML, WIA, dexplorer and WMI. + * + * DispHelper is free open source software provided under the BSD license. + * + * Find out more, browse the readable version of the source code + * and download DispHelper at: + * http://sourceforge.net/projects/disphelper/ + * http://disphelper.sourceforge.net/ + */ + + +/* To use DispHelper in your project, include this file(disphelper.c) and the + * header (disphelper.h). For Visual C++, Borland C++ and LCC-Win32 import + * libraries are included via pragma directives. For other compilers you may + * need to add ole32, oleaut32 and uuid. To do this in Dev-CPP add + * "-lole32 -loleaut32 -luuid" to the linker box under Project->Project Options->Parameters. + */ + + +/* If you are using Dev-CPP and get errors when compiling this file: + * Make sure this file is set to compile as C and not C++ under + * Project->Project Options->Files. + */ + + +#define DISPHELPER_INTERNAL_BUILD +#include "disphelper.h" +#include +#include + +/* ----- convert.h ----- */ + +HRESULT ConvertFileTimeToVariantTime(FILETIME * pft, DATE * pDate); +HRESULT ConvertVariantTimeToFileTime(DATE date, FILETIME * pft); + +HRESULT ConvertVariantTimeToSystemTime(DATE date, SYSTEMTIME * pSystemTime); +HRESULT ConvertSystemTimeToVariantTime(SYSTEMTIME * pSystemTime, DATE * pDate); + +HRESULT ConvertTimeTToVariantTime(time_t timeT, DATE * pDate); +HRESULT ConvertVariantTimeToTimeT(DATE date, time_t * pTimeT); + +HRESULT ConvertAnsiStrToBStr(LPCSTR szAnsiIn, BSTR * lpBstrOut); +HRESULT ConvertBStrToAnsiStr(BSTR bstrIn, LPSTR * lpszOut); + +/* ----- dh_create.c ----- */ + +HRESULT dhCreateObjectEx(LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, + COSERVERINFO * pServerInfo, void ** ppv) +{ + CLSID clsid; + HRESULT hr; + IClassFactory * pCf = NULL; + + DH_ENTER(L"CreateObjectEx"); + + if (!szProgId || !riid || !ppv) return DH_EXIT(E_INVALIDARG, szProgId); + + if (L'{' == szProgId[0]) + hr = CLSIDFromString((LPOLESTR) szProgId, &clsid); + else + hr = CLSIDFromProgID(szProgId, &clsid); + + if (SUCCEEDED(hr)) hr = CoGetClassObject(&clsid, dwClsContext, pServerInfo, &IID_IClassFactory, (void **) &pCf); + if (SUCCEEDED(hr)) hr = pCf->lpVtbl->CreateInstance(pCf, NULL, riid, ppv); + + if (pCf) pCf->lpVtbl->Release(pCf); + + return DH_EXIT(hr, szProgId); +} + +HRESULT dhGetObjectEx(LPCOLESTR szPathName, LPCOLESTR szProgId, REFIID riid, + DWORD dwClsContext, LPVOID lpvReserved, void ** ppv) +{ + HRESULT hr; + + DH_ENTER(L"GetObjectEx"); + + if ((!szProgId && !szPathName) || !riid || !ppv || lpvReserved) return DH_EXIT(E_INVALIDARG, szProgId); + + if (szPathName) + { + + if (!szProgId) + { + hr = CoGetObject(szPathName, NULL, riid, ppv); + } + else + { + IPersistFile * ppf = NULL; + + hr = dhCreateObjectEx(szProgId, &IID_IPersistFile, dwClsContext, NULL, (void **) &ppf); + + if (SUCCEEDED(hr)) hr = ppf->lpVtbl->Load(ppf, szPathName, 0); + if (SUCCEEDED(hr)) hr = ppf->lpVtbl->QueryInterface(ppf, riid, ppv); + + if (ppf) ppf->lpVtbl->Release(ppf); + } + } + else + { + + CLSID clsid; + IUnknown * pUnk = NULL; + + if (L'{' == szProgId[0]) + hr = CLSIDFromString((LPOLESTR) szProgId, &clsid); + else + hr = CLSIDFromProgID(szProgId, &clsid); + + if (SUCCEEDED(hr)) hr = GetActiveObject(&clsid, NULL, &pUnk); + if (SUCCEEDED(hr)) hr = pUnk->lpVtbl->QueryInterface(pUnk, riid, ppv); + + if (pUnk) pUnk->lpVtbl->Release(pUnk); + } + + return DH_EXIT(hr, szProgId); +} + +HRESULT dhCreateObject(LPCOLESTR szProgId, LPCWSTR szMachine, IDispatch ** ppDisp) +{ + COSERVERINFO si = { 0 }; + + DH_ENTER(L"CreateObject"); + + si.pwszName = (LPWSTR) szMachine; + + return DH_EXIT(dhCreateObjectEx(szProgId, &IID_IDispatch, + szMachine ? CLSCTX_REMOTE_SERVER : CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER, + szMachine ? &si : NULL, (void **) ppDisp), szProgId); +} + +HRESULT dhGetObject(LPCOLESTR szPathName, LPCOLESTR szProgId, IDispatch ** ppDisp) +{ + DH_ENTER(L"GetObject"); + + return DH_EXIT(dhGetObjectEx(szPathName, szProgId, &IID_IDispatch, + CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER, NULL, (void **) ppDisp), szProgId); +} + +HRESULT dhCallMethod(IDispatch * pDisp, LPCOLESTR szMember, ... ) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"CallMethod"); + + va_start(marker, szMember); + + hr = dhCallMethodV(pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +HRESULT dhPutValue(IDispatch * pDisp, LPCOLESTR szMember, ...) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"PutValue"); + + va_start(marker, szMember); + + hr = dhPutValueV(pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +HRESULT dhPutRef(IDispatch * pDisp, LPCOLESTR szMember, ...) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"PutRef"); + + va_start(marker, szMember); + + hr = dhPutRefV(pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +HRESULT dhGetValue(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, ...) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"GetValue"); + + va_start(marker, szMember); + + hr = dhGetValueV(szIdentifier, pResult, pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +HRESULT dhInvoke(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, ...) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"Invoke"); + + va_start(marker, szMember); + + hr = dhInvokeV(invokeType, returnType, pvResult, pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +/* ----- dh_core.c ----- */ + +BOOL dh_g_bIsUnicodeMode; + +HRESULT dhInvokeArray(int invokeType, VARIANT * pvResult, UINT cArgs, + IDispatch * pDisp, LPCOLESTR szMember, VARIANT * pArgs) +{ + DISPPARAMS dp = { 0 }; + EXCEPINFO excep = { 0 }; + DISPID dispidNamed = DISPID_PROPERTYPUT; + DISPID dispID; + UINT uiArgErr; + HRESULT hr; + + DH_ENTER(L"InvokeArray"); + + if(!pDisp || !szMember || (cArgs != 0 && !pArgs)) return DH_EXIT(E_INVALIDARG, szMember); + + hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, &IID_NULL, (LPOLESTR *) &szMember, 1, LOCALE_USER_DEFAULT, &dispID); + + if(FAILED(hr)) return DH_EXITEX(hr, TRUE, szMember, szMember, NULL, 0); + + if (pvResult != NULL) VariantInit(pvResult); + + dp.cArgs = cArgs; + dp.rgvarg = pArgs; + + if(invokeType & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)) + { + dp.cNamedArgs = 1; + dp.rgdispidNamedArgs = &dispidNamed; + } + + hr = pDisp->lpVtbl->Invoke(pDisp, dispID, &IID_NULL, LOCALE_USER_DEFAULT, (WORD) invokeType, &dp, pvResult, &excep, &uiArgErr); + + return DH_EXITEX(hr, TRUE, szMember, szMember, &excep, uiArgErr); +} + +HRESULT dhCallMethodV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + DH_ENTER(L"CallMethodV"); + + return DH_EXIT(dhInvokeV(DISPATCH_METHOD, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); +} + +HRESULT dhPutValueV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + DH_ENTER(L"PutValueV"); + + return DH_EXIT(dhInvokeV(DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); +} + +HRESULT dhPutRefV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + DH_ENTER(L"PutRefV"); + + return DH_EXIT(dhInvokeV(DISPATCH_PROPERTYPUTREF, VT_EMPTY, NULL, pDisp, szMember, marker), szMember); +} + +HRESULT dhGetValueV(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + VARIANT vtResult; + VARTYPE returnType; + HRESULT hr; + + DH_ENTER(L"GetValueV"); + + if (!pResult || !szIdentifier) return DH_EXIT(E_INVALIDARG, szMember); + + if (*szIdentifier == L'%') szIdentifier++; + + switch(*szIdentifier) + { + case L'd': returnType = VT_I4; break; + case L'u': returnType = VT_UI4; break; + case L'e': returnType = VT_R8; break; + case L'b': returnType = VT_BOOL; break; + case L'v': returnType = VT_EMPTY; break; + case L'B': returnType = VT_BSTR; break; + case L'S': returnType = VT_BSTR; break; + case L's': returnType = VT_BSTR; break; + case L'T': returnType = VT_BSTR; break; + case L'o': returnType = VT_DISPATCH; break; + case L'O': returnType = VT_UNKNOWN; break; + case L't': returnType = VT_DATE; break; + case L'W': returnType = VT_DATE; break; + case L'f': returnType = VT_DATE; break; + case L'D': returnType = VT_DATE; break; +#ifndef _WIN64 + case L'p': returnType = VT_I4; break; +#else + case L'p': returnType = VT_I8; break; +#endif + default: + DEBUG_NOTIFY_INVALID_IDENTIFIER(*szIdentifier); + return DH_EXIT(E_INVALIDARG, szMember); + } + + hr = dhInvokeV(DISPATCH_PROPERTYGET|DISPATCH_METHOD, returnType, &vtResult, pDisp, szMember, marker); + if (FAILED(hr)) return DH_EXIT(hr, szMember); + + switch(*szIdentifier) + { + case L'd': + *((LONG *) pResult) = V_I4(&vtResult); + break; + + case L'u': + *((ULONG *) pResult) = V_UI4(&vtResult); + break; + + case L'e': + *((DOUBLE *) pResult) = V_R8(&vtResult); + break; + + case L'b': + *((BOOL *) pResult) = V_BOOL(&vtResult); + break; + + case L'v': + *((VARIANT *) pResult) = vtResult; + break; + + case L'B': + *((BSTR *) pResult) = V_BSTR(&vtResult); + break; + + case L'S': + *((LPWSTR *) pResult) = V_BSTR(&vtResult); + break; + + case L's': + hr = ConvertBStrToAnsiStr(V_BSTR(&vtResult), (LPSTR *) pResult); + SysFreeString(V_BSTR(&vtResult)); + break; + + case L'T': + if (dh_g_bIsUnicodeMode) + { + *((LPWSTR *) pResult) = V_BSTR(&vtResult); + } + else + { + hr = ConvertBStrToAnsiStr(V_BSTR(&vtResult), (LPSTR *) pResult); + SysFreeString(V_BSTR(&vtResult)); + } + break; + + case L'o': + *((IDispatch **) pResult) = V_DISPATCH(&vtResult); + if (V_DISPATCH(&vtResult) == NULL) hr = E_NOINTERFACE; + break; + + case L'O': + *((IUnknown **) pResult) = V_UNKNOWN(&vtResult); + if (V_UNKNOWN(&vtResult) == NULL) hr = E_NOINTERFACE; + break; + + case L't': + hr = ConvertVariantTimeToTimeT(V_DATE(&vtResult), (time_t *) pResult); + break; + + case L'W': + hr = ConvertVariantTimeToSystemTime(V_DATE(&vtResult), (SYSTEMTIME *) pResult); + break; + + case L'f': + hr = ConvertVariantTimeToFileTime(V_DATE(&vtResult), (FILETIME *) pResult); + break; + + case L'D': + *((DATE *) pResult) = V_DATE(&vtResult); + break; + + case L'p': +#ifndef _WIN64 + *((LPVOID *) pResult) = (LPVOID) V_I4(&vtResult); +#else + *((LPVOID *) pResult) = (LPVOID) V_I8(&vtResult); +#endif + break; + } + + return DH_EXIT(hr, szMember); +} + +/* ----- dh_invoke.c ----- */ + +static HRESULT TraverseSubObjects(IDispatch ** ppDisp, LPWSTR * lpszMember, va_list * marker); +static HRESULT CreateArgumentArray(LPWSTR szTemp, VARIANT * pArgs, BOOL * pbFreeList, UINT * pcArgs, va_list * marker); +static HRESULT InternalInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR szMember, va_list * marker); +static HRESULT ExtractArgument(VARIANT * pvArg, WCHAR chIdentifier, BOOL * pbFreeArg, va_list * marker); + +HRESULT dhInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, + IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + WCHAR szCopy[DH_MAX_MEMBER]; + LPWSTR szTemp = szCopy; + SIZE_T cchDest = ARRAYSIZE(szCopy); + HRESULT hr; + + DH_ENTER(L"InvokeV"); + + if (!pDisp || !szMember || !marker) return DH_EXIT(E_INVALIDARG, szMember); + + do + { + if (cchDest-- == 0) return DH_EXIT(E_INVALIDARG, szMember); + } + while( (*szTemp++ = *szMember++) ); + + szTemp = szCopy; + + hr = TraverseSubObjects(&pDisp, &szTemp, marker); + + if (SUCCEEDED(hr)) + { + hr = InternalInvokeV(invokeType, returnType, pvResult, pDisp, szTemp, marker); + + pDisp->lpVtbl->Release(pDisp); + } + + return DH_EXIT(hr, szMember); +} + +static HRESULT TraverseSubObjects(IDispatch ** ppDisp, LPWSTR * lpszMember, va_list * marker) +{ + LPWSTR szSeperator, szTemp; + VARIANT vtObject; + HRESULT hr; + + DH_ENTER(L"TraverseSubObjects"); + + if (**lpszMember == L'.') (*lpszMember)++; + + (*ppDisp)->lpVtbl->AddRef(*ppDisp); + + szSeperator = wcschr(*lpszMember, L'.'); + + if (szSeperator == NULL) return DH_EXIT(NOERROR, *lpszMember); + + szTemp = *lpszMember; + + do + { + *szSeperator = L'\0'; + + hr = InternalInvokeV(DISPATCH_METHOD|DISPATCH_PROPERTYGET, VT_DISPATCH, + &vtObject, *ppDisp, szTemp, marker); + + if (!vtObject.pdispVal && SUCCEEDED(hr)) hr = E_NOINTERFACE; + + (*ppDisp)->lpVtbl->Release(*ppDisp); + + if (FAILED(hr)) break; + + *ppDisp = vtObject.pdispVal; + + szTemp = szSeperator + 1; + + } + while ( (szSeperator = wcschr(szTemp, L'.') ) != NULL); + + *lpszMember = szTemp; + + return DH_EXIT(hr, *lpszMember); +} + +static HRESULT InternalInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, + IDispatch * pDisp, LPOLESTR szMember, va_list * marker) +{ + VARIANT vtArgs[DH_MAX_ARGS]; + BOOL bFreeList[DH_MAX_ARGS]; + HRESULT hr; + UINT cArgs, iArg; + + DH_ENTER(L"InternalInvokeV"); + + hr = CreateArgumentArray(szMember, vtArgs, bFreeList, &cArgs, marker); + + if (SUCCEEDED(hr)) + { + hr = dhInvokeArray(invokeType, pvResult, cArgs, pDisp, szMember, &vtArgs[DH_MAX_ARGS - cArgs]); + + for (iArg = DH_MAX_ARGS - cArgs;iArg < DH_MAX_ARGS;iArg++) + { + if (bFreeList[iArg]) VariantClear(&vtArgs[iArg]); + } + + if (SUCCEEDED(hr) && pvResult != NULL && + pvResult->vt != returnType && returnType != VT_EMPTY) + { + hr = VariantChangeType(pvResult, pvResult, 16 , returnType); + if (FAILED(hr)) VariantClear(pvResult); + } + } + + return DH_EXIT(hr, szMember); +} + +static HRESULT CreateArgumentArray(LPWSTR szMember, VARIANT * pArgs, BOOL * pbFreeList, + UINT * pcArgs, va_list * marker) +{ + HRESULT hr = NOERROR; + INT iArg = DH_MAX_ARGS; + BOOL bInArguments = FALSE; + + DH_ENTER(L"CreateArgumentArray"); + + while (*szMember) + { + if (!bInArguments && + (*szMember == L'(' || *szMember == L' ' || *szMember == L'=') ) + { + bInArguments = TRUE; + + *szMember = L'\0'; + } + else if (*szMember == L'%') + { + if (!bInArguments) + { + bInArguments = TRUE; + *szMember = L'\0'; + } + + iArg--; + + if (iArg == -1) { hr = E_INVALIDARG; break; } + + szMember++; + + hr = ExtractArgument(&pArgs[iArg], *szMember, &pbFreeList[iArg], marker); + + if (FAILED(hr)) break; + } + + szMember++; + } + + *pcArgs = DH_MAX_ARGS - iArg; + + if (FAILED(hr)) + { + for (++iArg;iArg < DH_MAX_ARGS; iArg++) + { + if (pbFreeList[iArg]) VariantClear(&pArgs[iArg]); + } + } + + return DH_EXIT(hr, szMember); +} + +static HRESULT ExtractArgument(VARIANT * pvArg, WCHAR chIdentifier, BOOL * pbFreeArg, va_list * marker) +{ + HRESULT hr = NOERROR; + + *pbFreeArg = FALSE; + + if (chIdentifier == L'T') chIdentifier = (dh_g_bIsUnicodeMode ? L'S' : L's'); + + switch (chIdentifier) + { + case L'd': + V_VT(pvArg) = VT_I4; + V_I4(pvArg) = va_arg(*marker, LONG); + break; + + case L'u': + V_VT(pvArg) = VT_UI4; + V_UI4(pvArg) = va_arg(*marker, ULONG); + break; + + case L'e': + V_VT(pvArg) = VT_R8; + V_R8(pvArg) = va_arg(*marker, DOUBLE); + break; + + case L'b': + V_VT(pvArg) = VT_BOOL; + V_BOOL(pvArg) = ( va_arg(*marker, BOOL) ? VARIANT_TRUE : VARIANT_FALSE ); + break; + + case L'v': + *pvArg = *va_arg(*marker, VARIANT *); + break; + + case L'm': + V_VT(pvArg) = VT_ERROR; + V_ERROR(pvArg) = DISP_E_PARAMNOTFOUND; + break; + + case L'B': + V_VT(pvArg) = VT_BSTR; + V_BSTR(pvArg) = va_arg(*marker, BSTR); + break; + + case L'S': + { + LPOLESTR szTemp = va_arg(*marker, LPOLESTR); + + V_VT(pvArg) = VT_BSTR; + V_BSTR(pvArg) = SysAllocString(szTemp); + + if (V_BSTR(pvArg) == NULL && szTemp != NULL) hr = E_OUTOFMEMORY; + + *pbFreeArg = TRUE; + break; + } + + case L's': + V_VT(pvArg) = VT_BSTR; + hr = ConvertAnsiStrToBStr(va_arg(*marker, LPSTR), &V_BSTR(pvArg)); + *pbFreeArg = TRUE; + break; + + case L'o': + V_VT(pvArg) = VT_DISPATCH; + V_DISPATCH(pvArg) = va_arg(*marker, IDispatch *); + break; + + case L'O': + V_VT(pvArg) = VT_UNKNOWN; + V_UNKNOWN(pvArg) = va_arg(*marker, IUnknown *); + break; + + case L'D': + V_VT(pvArg) = VT_DATE; + V_DATE(pvArg) = va_arg(*marker, DATE); + break; + + case L't': + V_VT(pvArg) = VT_DATE; + hr = ConvertTimeTToVariantTime(va_arg(*marker, time_t), &V_DATE(pvArg)); + break; + + case L'W': + V_VT(pvArg) = VT_DATE; + hr = ConvertSystemTimeToVariantTime(va_arg(*marker, SYSTEMTIME *), &V_DATE(pvArg)); + break; + + case L'f': + V_VT(pvArg) = VT_DATE; + hr = ConvertFileTimeToVariantTime(va_arg(*marker, FILETIME *), &V_DATE(pvArg)); + break; + + case L'p': +#ifndef _WIN64 + V_VT(pvArg) = VT_I4; + V_I4(pvArg) = (LONG) va_arg(*marker, LPVOID); +#else + V_VT(pvArg) = VT_I8; + V_I8(pvArg) = (LONGLONG) va_arg(*marker, LPVOID); +#endif + break; + + default: + hr = E_INVALIDARG; + DEBUG_NOTIFY_INVALID_IDENTIFIER(chIdentifier); + break; + } + + return hr; +} + +/* ----- dh_enum.c ----- */ + +HRESULT dhEnumBeginV(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker) +{ + DISPPARAMS dp = { 0 }; + EXCEPINFO excep = { 0 }; + VARIANT vtResult; + IDispatch * pDispObj; + HRESULT hr; + + DH_ENTER(L"EnumBeginV"); + + if (!ppEnum || !pDisp) return DH_EXIT(E_INVALIDARG, szMember); + + if (szMember != NULL) + { + hr = dhGetValueV(L"%o", &pDispObj, pDisp, szMember, marker); + if (FAILED(hr)) return DH_EXIT(hr, szMember); + } + else + { + pDispObj = pDisp; + } + + hr = pDispObj->lpVtbl->Invoke(pDispObj, DISPID_NEWENUM, &IID_NULL, LOCALE_USER_DEFAULT, + DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dp, &vtResult, &excep, NULL); + + if (szMember != NULL) pDispObj->lpVtbl->Release(pDispObj); + + if (FAILED(hr)) return DH_EXITEX(hr, TRUE, L"_NewEnum", szMember, &excep, 0); + + if (vtResult.vt == VT_DISPATCH) + hr = vtResult.pdispVal->lpVtbl->QueryInterface(vtResult.pdispVal, &IID_IEnumVARIANT, (void **) ppEnum); + else if (vtResult.vt == VT_UNKNOWN) + hr = vtResult.punkVal->lpVtbl->QueryInterface(vtResult.punkVal, &IID_IEnumVARIANT, (void **) ppEnum); + else + hr = E_NOINTERFACE; + + VariantClear(&vtResult); + + return DH_EXIT(hr, szMember); +} + +HRESULT dhEnumNextVariant(IEnumVARIANT * pEnum, VARIANT * pvResult) +{ + DH_ENTER(L"EnumNextVariant"); + + if (!pEnum || !pvResult) return DH_EXIT(E_INVALIDARG, L"Enumerator"); + + return DH_EXIT(pEnum->lpVtbl->Next(pEnum, 1, pvResult, NULL), L"Enumerator"); +} + +HRESULT dhEnumNextObject(IEnumVARIANT * pEnum, IDispatch ** ppDisp) +{ + VARIANT vtResult; + HRESULT hr; + + DH_ENTER(L"EnumNextObject"); + + if (!pEnum || !ppDisp) return DH_EXIT(E_INVALIDARG, L"Enumerator"); + + hr = pEnum->lpVtbl->Next(pEnum, 1, &vtResult, NULL); + + if (hr == S_OK) + { + if (vtResult.vt == VT_DISPATCH) + { + *ppDisp = vtResult.pdispVal; + } + else + { + hr = VariantChangeType(&vtResult, &vtResult, 0, VT_DISPATCH); + if (SUCCEEDED(hr)) *ppDisp = vtResult.pdispVal; + else VariantClear(&vtResult); + } + } + + return DH_EXIT(hr, L"Enumerator"); +} + +HRESULT dhEnumBegin(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, ...) +{ + HRESULT hr; + va_list marker; + + DH_ENTER(L"EnumBegin"); + + va_start(marker, szMember); + + hr = dhEnumBeginV(ppEnum, pDisp, szMember, &marker); + + va_end(marker); + + return DH_EXIT(hr, szMember); +} + +/* ----- convert.c ----- */ + +static const LONGLONG FILE_TIME_ONE_DAY = 864000000000ULL; + +static const LONGLONG FILE_TIME_VARIANT_DAY0 = 94353120000000000ULL; + +static const ULONGLONG FILE_TIME_VARIANT_OVERFLOW = 2650467744000000000ULL; + +static const DATE VARIANT_FILE_TIME_DAY0 = -109205; + +static const DATE VARIANT_TIMET_DAY0 = 25569; + +static const LONG TIMET_ONE_DAY = 86400; + +#ifndef _WIN64 +static const DATE VARIANT_TIMET_MAX = 50424.13480; +#else +static const time_t TIMET_VARIANT_OVERFLOW = 253402300800; +#endif + +HRESULT ConvertFileTimeToVariantTime(FILETIME * pft, DATE * pDate) +{ + ULONGLONG ftScalar; + + if (!pft || !pDate) return E_INVALIDARG; + + ftScalar = *((ULONGLONG *) pft) + 500; + + if (ftScalar >= FILE_TIME_VARIANT_OVERFLOW) return E_INVALIDARG; + *pDate = (LONGLONG) (ftScalar - FILE_TIME_VARIANT_DAY0) / (double) FILE_TIME_ONE_DAY; + if (*pDate < 0) *pDate = floor(*pDate) + (floor(*pDate) - *pDate); + + return NOERROR; +} + +HRESULT ConvertVariantTimeToFileTime(DATE date, FILETIME * pft) +{ + ULONGLONG ftScalar; + + if (!pft) return E_INVALIDARG; + + if (date < 0) date = ceil(date) + (ceil(date) - date); + + if (date < VARIANT_FILE_TIME_DAY0) return E_INVALIDARG; + ftScalar = (ULONGLONG) ((date * FILE_TIME_ONE_DAY) + FILE_TIME_VARIANT_DAY0); + + *pft = *((FILETIME *) &ftScalar); + + return NOERROR; +} + +HRESULT ConvertVariantTimeToSystemTime(DATE date, SYSTEMTIME * pSystemTime) +{ + HRESULT hr; + FILETIME fileTime; + + if (!pSystemTime) return E_INVALIDARG; + if (FAILED(hr = ConvertVariantTimeToFileTime(date, &fileTime))) return hr; + return (FileTimeToSystemTime(&fileTime, pSystemTime) ? NOERROR : HRESULT_FROM_WIN32( GetLastError() )); +} + +HRESULT ConvertSystemTimeToVariantTime(SYSTEMTIME * pSystemTime, DATE * pDate) +{ + FILETIME fileTime; + + if (!pSystemTime || !pDate) return E_INVALIDARG; + if (!SystemTimeToFileTime(pSystemTime, &fileTime)) return HRESULT_FROM_WIN32( GetLastError() ); + return ConvertFileTimeToVariantTime(&fileTime, pDate); +} + +HRESULT ConvertVariantTimeToTimeT(DATE date, time_t * pTimeT) +{ + struct tm * ptm; + + if (!pTimeT) return E_INVALIDARG; + +#ifndef _WIN64 + if (date < VARIANT_TIMET_DAY0 || date > VARIANT_TIMET_MAX) return E_INVALIDARG; +#else + if (date < VARIANT_TIMET_DAY0) return E_INVALIDARG; +#endif + + *pTimeT = (time_t) (((date - VARIANT_TIMET_DAY0) * TIMET_ONE_DAY) + 0.5); + + if ( (ptm = gmtime(pTimeT)) == NULL || !(ptm->tm_isdst = -1) || + (*pTimeT = mktime(ptm)) == (time_t) -1 ) return E_FAIL; + + return NOERROR; +} + +HRESULT ConvertTimeTToVariantTime(time_t timeT, DATE * pDate) +{ + struct tm localtm, utctm, * ptm; + time_t timeTLocal, timeTUtc; + + if (!pDate) return E_INVALIDARG; + + if ( (ptm = localtime(&timeT)) == NULL) return E_FAIL; + localtm = *ptm; + + if ( (ptm = gmtime(&timeT)) == NULL) return E_FAIL; + utctm = *ptm; + + localtm.tm_isdst = 0; + utctm.tm_isdst = 0; + + if ( (timeTLocal = mktime(&localtm)) == (time_t) -1 || + (timeTUtc = mktime(&utctm)) == (time_t) -1) return E_FAIL; + + timeT += timeTLocal - timeTUtc; + +#ifdef _WIN64 + if (timeT >= TIMET_VARIANT_OVERFLOW) return E_INVALIDARG; +#endif + *pDate = (DATE) (timeT / (double) TIMET_ONE_DAY) + VARIANT_TIMET_DAY0; + + return NOERROR; +} + +HRESULT ConvertAnsiStrToBStr(LPCSTR szAnsiIn, BSTR * lpBstrOut) +{ + DWORD dwSize; + + if (lpBstrOut == NULL) return E_INVALIDARG; + if (szAnsiIn == NULL) { *lpBstrOut = NULL; return NOERROR; } + + dwSize = MultiByteToWideChar(CP_ACP, 0, szAnsiIn, -1, NULL, 0); + if (dwSize == 0) return HRESULT_FROM_WIN32( GetLastError() ); + + *lpBstrOut = SysAllocStringLen(NULL, dwSize - 1); + if (*lpBstrOut == NULL) return E_OUTOFMEMORY; + + if ( !MultiByteToWideChar(CP_ACP, 0, szAnsiIn, -1, *lpBstrOut, dwSize) ) + { + SysFreeString(*lpBstrOut); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + return NOERROR; +} + +HRESULT ConvertBStrToAnsiStr(BSTR bstrIn, LPSTR * lpszOut) +{ + DWORD dwSize; + + if (lpszOut == NULL) return E_INVALIDARG; + if (bstrIn == NULL) { *lpszOut = NULL; return NOERROR; } + + dwSize = WideCharToMultiByte(CP_ACP, 0, bstrIn, -1, NULL, 0, NULL, NULL); + if (dwSize == 0) return HRESULT_FROM_WIN32( GetLastError() ); + + *lpszOut = (LPSTR) SysAllocStringByteLen(NULL, dwSize - 1); + if (*lpszOut == NULL) return E_OUTOFMEMORY; + + if ( !WideCharToMultiByte(CP_ACP, 0, bstrIn, -1, *lpszOut, dwSize, NULL, NULL) ) + { + SysFreeString((BSTR) *lpszOut); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + return NOERROR; +} + +/* ----- dh_exceptions.c ----- */ + +#ifndef DISPHELPER_NO_EXCEPTIONS + +static DH_EXCEPTION_OPTIONS g_ExceptionOptions; + +static LONG f_lngTlsInitBegin = -1, f_lngTlsInitEnd = -1; +static DWORD f_TlsIdxStackCount, f_TlsIdxException; + +#define SetStackCount(nStackCount) TlsSetValue(f_TlsIdxStackCount, (LPVOID) (nStackCount)) +#define SetExceptionPtr(pException) TlsSetValue(f_TlsIdxException, pException); +#define GetStackCount() (UINT) TlsGetValue(f_TlsIdxStackCount) +#define GetExceptionPtr() TlsGetValue(f_TlsIdxException) +#define CheckTlsInitialized() if (f_lngTlsInitEnd != 0) InitializeTlsIndexes(); + +static void hlprStringCchCopyW(LPWSTR pszDest, SIZE_T cchDest, LPCWSTR pszSrc) +{ + assert(cchDest > 0); + + do + { + if (--cchDest == 0) break; + } + while ((*pszDest++ = *pszSrc++)); + + *pszDest = L'\0'; +} + +static void InitializeTlsIndexes(void) +{ + if (0 == InterlockedIncrement(&f_lngTlsInitBegin)) + { + f_TlsIdxStackCount = TlsAlloc(); + f_TlsIdxException = TlsAlloc(); + f_lngTlsInitEnd = 0; + } + else + { + while (f_lngTlsInitEnd != 0) Sleep(5); + } +} + +void dhEnter(void) +{ + CheckTlsInitialized(); + SetStackCount(GetStackCount() + 1); +} + +HRESULT dhExitEx(HRESULT hr, BOOL bDispatchError, LPCWSTR szMember, LPCWSTR szCompleteMember, + EXCEPINFO * pExcepInfo, UINT iArgError, LPCWSTR szFunctionName) +{ + UINT nStackCount = GetStackCount(); + + SetStackCount(nStackCount - 1); + + if (FAILED(hr) && !g_ExceptionOptions.bDisableRecordExceptions) + { + PDH_EXCEPTION pException = GetExceptionPtr(); + + if (!pException) + { + pException = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DH_EXCEPTION)); + if (!pException) return hr; + SetExceptionPtr(pException); + } + else if (pException->bOld) + { + SysFreeString(pException->szDescription); + SysFreeString(pException->szSource); + SysFreeString(pException->szHelpFile); + ZeroMemory(pException, sizeof(DH_EXCEPTION)); + } + + if (pException->hr == 0) + { + pException->hr = hr; + pException->iArgError = iArgError; + pException->szErrorFunction = szFunctionName; + pException->bDispatchError = bDispatchError; + + if (szMember) hlprStringCchCopyW(pException->szMember, ARRAYSIZE(pException->szMember), szMember); + + if (pExcepInfo && hr == DISP_E_EXCEPTION) + { + if (pExcepInfo->pfnDeferredFillIn && + !IsBadCodePtr((FARPROC) pExcepInfo->pfnDeferredFillIn)) pExcepInfo->pfnDeferredFillIn(pExcepInfo); + + pException->szDescription = pExcepInfo->bstrDescription; + pException->szSource = pExcepInfo->bstrSource; + pException->szHelpFile = pExcepInfo->bstrHelpFile; + pException->dwHelpContext = pExcepInfo->dwHelpContext; + pException->swCode = (pExcepInfo->wCode ? pExcepInfo->wCode : pExcepInfo->scode); + } + } + + if (nStackCount == 1) + { + pException->bOld = TRUE; + pException->szInitialFunction = szFunctionName; + + if (szCompleteMember) hlprStringCchCopyW(pException->szCompleteMember, ARRAYSIZE(pException->szCompleteMember), szCompleteMember); + + if (g_ExceptionOptions.bShowExceptions) + dhShowException(pException); + + if (g_ExceptionOptions.pfnExceptionCallback) + g_ExceptionOptions.pfnExceptionCallback(pException); + } + } + else if (hr == DISP_E_EXCEPTION && pExcepInfo) + { + SysFreeString(pExcepInfo->bstrDescription); + SysFreeString(pExcepInfo->bstrSource); + SysFreeString(pExcepInfo->bstrHelpFile); + } + + return hr; +} + +HRESULT dhShowException(PDH_EXCEPTION pException) +{ + WCHAR szMessage[512]; + + dhFormatExceptionW(pException, szMessage, ARRAYSIZE(szMessage), FALSE); + + MessageBoxW(g_ExceptionOptions.hwnd, szMessage, g_ExceptionOptions.szAppName, + MB_ICONSTOP | MB_SETFOREGROUND); + + return NOERROR; +} + +HRESULT dhFormatExceptionW(PDH_EXCEPTION pException, LPWSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont) +{ + HRESULT hr; + UINT cch = 0; +# define DESCRIPTION_LENGTH 255 + + if (!szBuffer && cchBufferSize) return E_INVALIDARG; + + if (!pException) + { + dhGetLastException(&pException); + if (!pException) + { + if (cchBufferSize != 0) + { + _snwprintf(szBuffer, cchBufferSize, L"No error information available."); + szBuffer[cchBufferSize - 1] = L'\0'; + } + + return NOERROR; + } + } + + hr = (pException->hr == DISP_E_EXCEPTION && pException->swCode ? + pException->swCode : pException->hr); + + if (!pException->szSource) + { + if (pException->bDispatchError) + pException->szSource = SysAllocString(L"IDispatch Interface"); + else + pException->szSource = SysAllocString(L"Application"); + } + + if (!pException->szDescription) + { + pException->szDescription = SysAllocStringLen(NULL, DESCRIPTION_LENGTH); + + if (pException->szDescription) + { + switch (hr) + { + case E_NOINTERFACE: + _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Object required"); + break; + + case DISP_E_UNKNOWNNAME: + case DISP_E_MEMBERNOTFOUND: + _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Object doesn't support this property or method: '%s'", pException->szMember); + break; + + case DISP_E_TYPEMISMATCH: + if (pException->szMember[0]) + { + _snwprintf(pException->szDescription, DESCRIPTION_LENGTH, L"Type mismatch: '%s'. Argument Index: %d", pException->szMember, pException->iArgError); + break; + } + + default: + { +#ifndef UNICODE + CHAR szDescription[DESCRIPTION_LENGTH]; +#else + LPWSTR szDescription = pException->szDescription; +#endif + cch = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + szDescription, DESCRIPTION_LENGTH, NULL); + + if (!cch) wcscpy(pException->szDescription, L"Unknown runtime error"); +#ifndef UNICODE + else MultiByteToWideChar(CP_ACP, 0, szDescription, -1, pException->szDescription, DESCRIPTION_LENGTH); +#endif + } + } + } + } + + if (pException->szDescription) + { + + if (!cch) cch = wcslen(pException->szDescription); + + if (cch >= 2 && pException->szDescription[cch - 2] == L'\r') + pException->szDescription[cch - 2] = L'\0'; + else if (cch >= 1 && pException->szDescription[cch - 1] == L'\n') + pException->szDescription[cch - 1] = L'\0'; + } + + if (cchBufferSize) + { + if (!bFixedFont) + { + _snwprintf(szBuffer, cchBufferSize, L"Member:\t %s\r\nFunction:\t %s\t\t\r\nError In:\t %s\r\nError:\t %s\r\nCode:\t %x\r\nSource:\t %s", + pException->szCompleteMember, + pException->szInitialFunction, pException->szErrorFunction, + pException->szDescription, hr, + pException->szSource); + } + else + { + _snwprintf(szBuffer, cchBufferSize, L"Member: %s\r\nFunction: %s\r\nError In: %s\r\nError: %s\r\nCode: %x\r\nSource: %s", + pException->szCompleteMember, + pException->szInitialFunction, pException->szErrorFunction, + pException->szDescription, hr, + pException->szSource); + } + + szBuffer[cchBufferSize - 1] = L'\0'; + } + + return NOERROR; +} + +HRESULT dhFormatExceptionA(PDH_EXCEPTION pException, LPSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont) +{ + WCHAR szBufferW[1024]; + + dhFormatExceptionW(pException, szBufferW, ARRAYSIZE(szBufferW), bFixedFont); + + if (0 == WideCharToMultiByte(CP_ACP, 0, szBufferW, -1, szBuffer, cchBufferSize, NULL, NULL)) + return HRESULT_FROM_WIN32( GetLastError() ); + + return NOERROR; +} + +HRESULT dhGetLastException(PDH_EXCEPTION * ppException) +{ + if (!ppException) return E_INVALIDARG; + + CheckTlsInitialized(); + *ppException = GetExceptionPtr(); + + return NOERROR; +} + +HRESULT dhToggleExceptions(BOOL bShow) +{ + g_ExceptionOptions.bShowExceptions = bShow; + if (bShow) g_ExceptionOptions.bDisableRecordExceptions = FALSE; + + return NOERROR; +} + +HRESULT dhSetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions) +{ + if (!pExceptionOptions) return E_INVALIDARG; + + g_ExceptionOptions.hwnd = pExceptionOptions->hwnd; + g_ExceptionOptions.szAppName = pExceptionOptions->szAppName; + g_ExceptionOptions.bShowExceptions = pExceptionOptions->bShowExceptions; + g_ExceptionOptions.bDisableRecordExceptions = pExceptionOptions->bDisableRecordExceptions; + g_ExceptionOptions.pfnExceptionCallback = pExceptionOptions->pfnExceptionCallback; + + return NOERROR; +} + +HRESULT dhGetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions) +{ + if (!pExceptionOptions) return E_INVALIDARG; + + pExceptionOptions->hwnd = g_ExceptionOptions.hwnd; + pExceptionOptions->szAppName = g_ExceptionOptions.szAppName; + pExceptionOptions->bShowExceptions = g_ExceptionOptions.bShowExceptions; + pExceptionOptions->bDisableRecordExceptions = g_ExceptionOptions.bDisableRecordExceptions; + pExceptionOptions->pfnExceptionCallback = g_ExceptionOptions.pfnExceptionCallback; + + return NOERROR; +} + +void dhCleanupThreadException(void) +{ + PDH_EXCEPTION pException; + + CheckTlsInitialized(); + pException = GetExceptionPtr(); + + if (pException) + { + SysFreeString(pException->szDescription); + SysFreeString(pException->szSource); + SysFreeString(pException->szHelpFile); + + HeapFree(GetProcessHeap(), 0, pException); + + SetExceptionPtr(NULL); + } +} + +#endif + +/* ----- dh_init.c ----- */ + +HRESULT dhInitializeImp(BOOL bInitializeCOM, BOOL bUnicode) +{ + dh_g_bIsUnicodeMode = bUnicode; + + if (bInitializeCOM) return CoInitialize(NULL); + + return NOERROR; +} + +void dhUninitialize(BOOL bUninitializeCOM) +{ +#ifndef DISPHELPER_NO_EXCEPTIONS + dhCleanupThreadException(); +#endif + if (bUninitializeCOM) CoUninitialize(); +} + diff --git a/pandora_agents/win32/windows/wmi/disphelper.h b/pandora_agents/win32/windows/wmi/disphelper.h new file mode 100644 index 0000000000..90b676079c --- /dev/null +++ b/pandora_agents/win32/windows/wmi/disphelper.h @@ -0,0 +1,731 @@ +/* This file is part of the source code for the DispHelper COM helper library. + * DispHelper allows you to call COM objects with an extremely simple printf style syntax. + * DispHelper can be used from C++ or even plain C. It works with most Windows compilers + * including Dev-CPP, Visual C++ and LCC-WIN32. Including DispHelper in your project + * couldn't be simpler as it is available in a compacted single file version. + * + * Included with DispHelper are over 20 samples that demonstrate using COM objects + * including ADO, CDO, Outlook, Eudora, Excel, Word, Internet Explorer, MSHTML, + * PocketSoap, Word Perfect, MS Agent, SAPI, MSXML, WIA, dexplorer and WMI. + * + * DispHelper is free open source software provided under the BSD license. + * + * Find out more and download DispHelper at: + * http://sourceforge.net/projects/disphelper/ + * http://disphelper.sourceforge.net/ + */ + + +#ifndef DISPHELPER_H_INCLUDED +#define DISPHELPER_H_INCLUDED + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT dhCreateObject(LPCOLESTR szProgId, LPCWSTR szMachine, IDispatch ** ppDisp); +HRESULT dhGetObject(LPCOLESTR szFile, LPCOLESTR szProgId, IDispatch ** ppDisp); + +HRESULT dhCreateObjectEx(LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, COSERVERINFO * pServerInfo, void ** ppv); +HRESULT dhGetObjectEx(LPCOLESTR szFile, LPCOLESTR szProgId, REFIID riid, DWORD dwClsContext, LPVOID lpvReserved, void ** ppv); + +HRESULT dhCallMethod(IDispatch * pDisp, LPCOLESTR szMember, ...); +HRESULT dhPutValue(IDispatch * pDisp, LPCOLESTR szMember, ...); +HRESULT dhPutRef(IDispatch * pDisp, LPCOLESTR szMember, ...); +HRESULT dhGetValue(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, ...); + +HRESULT dhInvoke(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, ...); +HRESULT dhInvokeArray(int invokeType, VARIANT * pvResult, UINT cArgs, IDispatch * pDisp, LPCOLESTR szMember, VARIANT * pArgs); + +HRESULT dhCallMethodV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); +HRESULT dhPutValueV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); +HRESULT dhPutRefV(IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); +HRESULT dhGetValueV(LPCWSTR szIdentifier, void * pResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); +HRESULT dhInvokeV(int invokeType, VARTYPE returnType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); + +HRESULT dhAutoWrap(int invokeType, VARIANT * pvResult, IDispatch * pDisp, LPCOLESTR szMember, UINT cArgs, ...); +HRESULT dhParseProperties(IDispatch * pDisp, LPCWSTR szProperties, UINT * lpcPropsSet); + +HRESULT dhEnumBegin(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, ...); +HRESULT dhEnumBeginV(IEnumVARIANT ** ppEnum, IDispatch * pDisp, LPCOLESTR szMember, va_list * marker); +HRESULT dhEnumNextObject(IEnumVARIANT * pEnum, IDispatch ** ppDisp); +HRESULT dhEnumNextVariant(IEnumVARIANT * pEnum, VARIANT * pvResult); + +HRESULT dhInitializeImp(BOOL bInitializeCOM, BOOL bUnicode); +void dhUninitialize(BOOL bUninitializeCOM); + +#define dhInitializeA(bInitializeCOM) dhInitializeImp(bInitializeCOM, FALSE) +#define dhInitializeW(bInitializeCOM) dhInitializeImp(bInitializeCOM, TRUE) + +#ifdef UNICODE +#define dhInitialize dhInitializeW +#else +#define dhInitialize dhInitializeA +#endif + +#define AutoWrap dhAutoWrap +#define DISPATCH_OBJ(objName) IDispatch * objName = NULL +#define dhFreeString(string) SysFreeString((BSTR) string) + +#ifndef SAFE_RELEASE +#ifdef __cplusplus +#define SAFE_RELEASE(pObj) { if (pObj) { (pObj)->Release(); (pObj) = NULL; } } +#else +#define SAFE_RELEASE(pObj) { if (pObj) { (pObj)->lpVtbl->Release(pObj); (pObj) = NULL; } } +#endif +#endif + +#define SAFE_FREE_STRING(string) { dhFreeString(string); (string) = NULL; } + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_WITH + +#define WITH0(objName, pDisp, szMember) { \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember))) { + +#define WITH1(objName, pDisp, szMember, arg1) { \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1))) { + +#define WITH2(objName, pDisp, szMember, arg1, arg2) { \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2))) { + +#define WITH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3))) { + +#define WITH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3, arg4))) { + +#define WITH WITH0 + +#define ON_WITH_ERROR(objName) } else { + +#define END_WITH(objName) } SAFE_RELEASE(objName); } + +#endif /* ----- DISPHELPER_NO_WITH ----- */ + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_FOR_EACH + +#define FOR_EACH0(objName, pDisp, szMember) { \ + IEnumVARIANT * xx_pEnum_xx = NULL; \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember))) { \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#define FOR_EACH1(objName, pDisp, szMember, arg1) { \ + IEnumVARIANT * xx_pEnum_xx = NULL; \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1))) { \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#define FOR_EACH2(objName, pDisp, szMember, arg1, arg2) { \ + IEnumVARIANT * xx_pEnum_xx = NULL; \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2))) { \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + + +#define FOR_EACH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ + IEnumVARIANT * xx_pEnum_xx = NULL; \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3))) { \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + + +#define FOR_EACH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ + IEnumVARIANT * xx_pEnum_xx = NULL; \ + DISPATCH_OBJ(objName); \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3, arg4))) { \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#define FOR_EACH FOR_EACH0 + +#define ON_FOR_EACH_ERROR(objName) SAFE_RELEASE(objName); }} else {{ + +#define NEXT(objName) SAFE_RELEASE(objName); }} SAFE_RELEASE(objName); SAFE_RELEASE(xx_pEnum_xx); } + +#endif /* ----- DISPHELPER_NO_FOR_EACH ----- */ + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_EXCEPTIONS + +/* Structure to store a DispHelper exception */ +typedef struct tagDH_EXCEPTION +{ + LPCWSTR szInitialFunction; + LPCWSTR szErrorFunction; + + HRESULT hr; + + WCHAR szMember[64]; + WCHAR szCompleteMember[256]; + + UINT swCode; + LPWSTR szDescription; + LPWSTR szSource; + LPWSTR szHelpFile; + DWORD dwHelpContext; + + UINT iArgError; + + BOOL bDispatchError; + +#ifdef DISPHELPER_INTERNAL_BUILD + BOOL bOld; +#endif +} DH_EXCEPTION, * PDH_EXCEPTION; + +typedef void (*DH_EXCEPTION_CALLBACK) (PDH_EXCEPTION); + +/* Structure to store exception options. */ +typedef struct tagDH_EXCEPTION_OPTIONS +{ + HWND hwnd; + LPCWSTR szAppName; + BOOL bShowExceptions; + BOOL bDisableRecordExceptions; + DH_EXCEPTION_CALLBACK pfnExceptionCallback; +} DH_EXCEPTION_OPTIONS, * PDH_EXCEPTION_OPTIONS; + +/* Functions to manipulate global exception options */ +HRESULT dhToggleExceptions(BOOL bShow); +HRESULT dhSetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions); +HRESULT dhGetExceptionOptions(PDH_EXCEPTION_OPTIONS pExceptionOptions); + +/* Functions to show an exception, format an exception into a string + * and get a copy of the last exception */ +HRESULT dhShowException(PDH_EXCEPTION pException); +HRESULT dhGetLastException(PDH_EXCEPTION * pException); +HRESULT dhFormatExceptionW(PDH_EXCEPTION pException, LPWSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont); +HRESULT dhFormatExceptionA(PDH_EXCEPTION pException, LPSTR szBuffer, UINT cchBufferSize, BOOL bFixedFont); + +#ifdef UNICODE +#define dhFormatException dhFormatExceptionW +#else +#define dhFormatException dhFormatExceptionA +#endif + +#ifdef DISPHELPER_INTERNAL_BUILD + +void dhEnter(void); +HRESULT dhExitEx(HRESULT hr, BOOL bDispatchError, LPCWSTR szMember, LPCWSTR szCompleteMember, EXCEPINFO * pExcepInfo, UINT iArgError, LPCWSTR szFunctionName); +void dhCleanupThreadException(void); + +#define DH_ENTER(szFunctionName) static LPCWSTR xx_szFunctionName_xx = szFunctionName; \ + dhEnter() + +#define DH_EXITEX(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError) \ + dhExitEx(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError, xx_szFunctionName_xx) + +#define DH_EXIT(hr, szCompleteMember) DH_EXITEX(hr, FALSE, NULL, szCompleteMember, NULL, 0) + +#endif /* ----- DISPHELPER_INTERNAL_BUILD ----- */ + +#else /* ----- DISPHELPER_NO_EXCEPTIONS ----- */ + +/* These macros define out calls to selected exception functions */ +#define dhToggleExceptions(bShow) (E_NOTIMPL) +#define dhSetExceptionOptions(pExcepOptions) (E_NOTIMPL) + +#ifdef DISPHELPER_INTERNAL_BUILD +#define DH_ENTER(szFunctionName) +#define DH_EXITEX(hr, bDispatchError, szMember, szCompleteMember, pExcepInfo, iArgError) \ + (((hr == DISP_E_EXCEPTION && pExcepInfo) ? \ + (SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrSource), \ + SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrDescription), \ + SysFreeString(((EXCEPINFO *)(pExcepInfo))->bstrHelpFile), 0) : (0)), hr) +#define DH_EXIT(hr, szCompleteMember)(hr) +#endif + +#endif /* ----- DISPHELPER_NO_EXCEPTIONS ----- */ + + + + + + +/* ===================================================================== */ +#ifdef DISPHELPER_INTERNAL_BUILD + +#include +#include +#include + +/* Macro to include or lose debug code. */ +#ifdef DEBUG +#define DBG_CODE(code) code +#else +#define DBG_CODE(code) +#endif + +/* Are we in unicode mode? */ +extern BOOL dh_g_bIsUnicodeMode; + +/* Number of objects in an array */ +#undef ARRAYSIZE +#define ARRAYSIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* Maximum number of arguments for a member */ +#define DH_MAX_ARGS 25 + +/* Maximum length of a member string */ +#define DH_MAX_MEMBER 512 + +/* This macro is missing from Dev-Cpp/Mingw */ +#ifndef V_UI4 +#define V_UI4(X) V_UNION(X, ulVal) +#endif + +/* Macro to notify programmer of invalid identifier in debug mode. */ +#define DEBUG_NOTIFY_INVALID_IDENTIFIER(chIdentifier) \ +DBG_CODE( { \ + char buf[256]; \ + sprintf(buf,"DEBUG: The format string or return identifier contained the invalid identifier '%c'.\n" \ + "The valid identifiers are \"d/u/e/b/v/B/S/s/T/o/O/t/W/D/f/m\".\n" \ + "Each %% character should be followed by a valid identifier.\n" \ + "Identifiers are case sensitive.", (chIdentifier)); \ + MessageBoxA(NULL, buf, "DEBUG: Invalid Format Identifier", MB_ICONSTOP); \ + } ) + +#ifdef _MSC_VER +#pragma warning(disable : 4706) /* Assignment in conditional expression */ +#endif + +#ifndef DISPHELPER_NO_PRAGMA_LIB +#ifdef __LCC__ +#pragma lib +#pragma lib +#pragma lib +#endif +#endif + + +#endif /* ----- DISPHELPER_INTERNAL_BUILD ----- */ + +#ifndef DISPHELPER_NO_PRAGMA_LIB +#if defined(_MSC_VER) || defined(__BORLANDC__) +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") +#pragma comment(lib, "uuid.lib") +#endif +#endif + + + +#ifdef __cplusplus +} +#endif + + + + +/* ===================================================================== */ +#if defined(__cplusplus) && !defined(DISPHELPER_NO_CPP_EXTENSIONS) + +#include +#include + +#ifdef _MSC_VER +#pragma warning( disable : 4290 ) /* throw() specification ignored */ +#endif + +#ifndef DISPHELPER_USE_MS_SMART_PTR + +template +class CDhComPtr +{ +public: + CDhComPtr() throw() : m_pInterface (NULL) {} + + CDhComPtr(T* pInterface) throw() : m_pInterface (pInterface) + { + if (m_pInterface) m_pInterface->AddRef(); + } + + CDhComPtr(const CDhComPtr& original) throw() : m_pInterface (original.m_pInterface) + { + if (m_pInterface) m_pInterface->AddRef(); + } + + ~CDhComPtr() throw() + { + Dispose(); + } + + void Dispose() throw() + { + if (m_pInterface) + { + m_pInterface->Release(); + m_pInterface = NULL; + } + } + + T* Detach() throw() + { + T* temp = m_pInterface; + m_pInterface = NULL; + return temp; + } + + inline operator T*() const throw() + { + return m_pInterface; + } + + T** operator&() throw() + { + Dispose(); + return &m_pInterface; + } + + T* operator->() const throw(HRESULT) + { + if (!m_pInterface) throw E_POINTER; + return m_pInterface; + } + + CDhComPtr& operator=(T* pInterface) throw() + { + if (m_pInterface != pInterface) + { + T* pOldInterface = m_pInterface; + m_pInterface = pInterface; + if (m_pInterface) m_pInterface->AddRef(); + if (pOldInterface) pOldInterface->Release(); + } + + return *this; + } + + CDhComPtr& operator=(const int null) throw(HRESULT) + { + if (null != 0) throw(E_POINTER); + return operator=((T*) NULL); + } + + CDhComPtr& operator=(const CDhComPtr& rhs) throw() + { + return operator=(rhs.m_pInterface); + } + +private: + T* m_pInterface; +}; + +typedef CDhComPtr CDispPtr; +typedef CDhComPtr CEnumPtr; +typedef CDhComPtr CUnknownPtr; + +#else /* DISPHELPER_USE_MS_SMART_PTR */ + +#include +typedef IDispatchPtr CDispPtr; +typedef IEnumVARIANTPtr CEnumPtr; +typedef IUnknownPtr CUnknownPtr; + +#endif /* DISPHELPER_USE_MS_SMART_PTR */ + + + + +/* ===================================================================== */ +template +class CDhStringTemplate +{ +public: + CDhStringTemplate() throw() : m_strptr (NULL) {} + + CDhStringTemplate(const CDhStringTemplate& original) throw() + { + Copy(original.m_strptr); + } + + CDhStringTemplate(const int null) throw(HRESULT) : m_strptr (NULL) + { + if (null != 0) throw(E_POINTER); + } + + ~CDhStringTemplate() throw() + { + Dispose(); + } + + void Dispose() throw() + { + dhFreeString(m_strptr); + m_strptr = NULL; + } + + T* Detach() throw() + { + T* temp = m_strptr; + m_strptr = NULL; + return temp; + } + + T** operator&() throw() + { + Dispose(); + return &m_strptr; + } + + inline operator T*() const throw() + { + return m_strptr; + } + + inline T& operator[](int nIndex) const throw() + { + return m_strptr[nIndex]; + } + + CDhStringTemplate& operator=(const CDhStringTemplate& rhs) + { + if (m_strptr != rhs.m_strptr) + { + T* temp = m_strptr; + Copy(rhs.m_strptr); + dhFreeString(temp); + } + + return *this; + } + + CDhStringTemplate& operator=(const int null) throw(HRESULT) + { + if (null != 0) throw(E_POINTER); + Dispose(); + return *this; + } + +private: + void Copy(const T* rhs) + { + if (rhs == NULL) + { + m_strptr = NULL; + } + else if (sizeof(T) == sizeof(CHAR)) + { + m_strptr = (T*) SysAllocStringByteLen((LPCSTR) rhs, SysStringByteLen((BSTR) rhs)); + } + else + { + m_strptr = (T*) SysAllocStringLen((OLECHAR *) rhs, SysStringLen((BSTR) rhs)); + } + } + + T* m_strptr; +}; + +typedef CDhStringTemplate CDhStringA; /* Ansi string - LPSTR */ +typedef CDhStringTemplate CDhStringW; /* Unicode string - LPWSTR */ +typedef CDhStringTemplate CDhStringB; /* Unicode bstring - BSTR */ +typedef CDhStringTemplate CDhStringT; /* T string - LPTSTR */ +typedef CDhStringTemplate CDhString; /* T string - LPTSTR */ + +inline std::ostream& operator<<(std::ostream& os, const CDhStringA& s) +{ + return os << (s ? s : (char*) "(null)"); +} + +//inline std::wostream& operator<<(std::wostream& os, const CDhStringW& s) +//{ +// return os << (s ? s : (wchar_t*) L"(null)"); +//} + + + + +/* ===================================================================== */ +class CDhInitialize +{ +public: + CDhInitialize(const BOOL bInitCom = TRUE) throw() : m_bInitCom (bInitCom) + { + dhInitialize(m_bInitCom); + } + + ~CDhInitialize() throw() + { + dhUninitialize(m_bInitCom); + } +private: + BOOL m_bInitCom; +}; + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_EXCEPTIONS +class dhThrowFunctions +{ +public: + static void throw_string() throw(std::string) + { + CHAR szMessage[512]; + dhFormatExceptionA(NULL, szMessage, sizeof(szMessage)/sizeof(szMessage[0]), TRUE); + throw std::string(szMessage); + } + + static void throw_wstring() throw(std::wstring) + { + WCHAR szMessage[512]; + dhFormatExceptionW(NULL, szMessage, sizeof(szMessage)/sizeof(szMessage[0]), TRUE); + throw std::wstring(szMessage); + } + + static void throw_dhexception() throw(PDH_EXCEPTION) + { + PDH_EXCEPTION pException = NULL; + dhGetLastException(&pException); + throw pException; + } +}; +#endif /* DISPHELPER_NO_EXCEPTIONS */ + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_EXCEPTIONS +inline bool dhIfFailThrowString(HRESULT hr) throw(std::string) +{ + if (FAILED(hr)) dhThrowFunctions::throw_string(); + return true; +} + +inline bool dhIfFailThrowWString(HRESULT hr) throw(std::wstring) +{ + if (FAILED(hr)) dhThrowFunctions::throw_wstring(); + return true; +} + +inline bool dhIfFailThrowDhException(HRESULT hr) throw(PDH_EXCEPTION) +{ + if (FAILED(hr)) dhThrowFunctions::throw_dhexception(); + return true; +} + +#define dhCheck dhIfFailThrowString + +#endif /* DISPHELPER_NO_EXCEPTIONS */ + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_WITH + +#undef WITH0 +#define WITH0(objName, pDisp, szMember) { \ + CDispPtr objName; \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember))) { + +#undef WITH1 +#define WITH1(objName, pDisp, szMember, arg1) { \ + CDispPtr objName; \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1))) { + +#undef WITH2 +#define WITH2(objName, pDisp, szMember, arg1, arg2) { \ + CDispPtr objName; \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2))) { + +#undef WITH3 +#define WITH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ + CDispPtr objName; \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3))) { + +#undef WITH4 +#define WITH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ + CDispPtr objName; \ + if (SUCCEEDED(dhGetValue(L"%o", &objName, pDisp, szMember, arg1, arg2, arg3, arg4))) { + +#undef ON_WITH_ERROR +#define ON_WITH_ERROR(objName) } else { + +#undef END_WITH +#define END_WITH(objName) }} + +#define END_WITH_THROW(objName) } else { dhThrowFunctions::throw_string(); }} + +#endif /* DISPHELPER_NO_WITH */ + + + + +/* ===================================================================== */ +#ifndef DISPHELPER_NO_FOR_EACH + +#undef FOR_EACH0 +#define FOR_EACH0(objName, pDisp, szMember) { \ + CEnumPtr xx_pEnum_xx; \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember))) { \ + CDispPtr objName; \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#undef FOR_EACH1 +#define FOR_EACH1(objName, pDisp, szMember, arg1) { \ + CEnumPtr xx_pEnum_xx; \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1))) { \ + CDispPtr objName; \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#undef FOR_EACH2 +#define FOR_EACH2(objName, pDisp, szMember, arg1, arg2) { \ + CEnumPtr xx_pEnum_xx; \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2))) { \ + CDispPtr objName; \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#undef FOR_EACH3 +#define FOR_EACH3(objName, pDisp, szMember, arg1, arg2, arg3) { \ + CEnumPtr xx_pEnum_xx; \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3))) { \ + CDispPtr objName; \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#undef FOR_EACH4 +#define FOR_EACH4(objName, pDisp, szMember, arg1, arg2, arg3, arg4) { \ + CEnumPtr xx_pEnum_xx; \ + if (SUCCEEDED(dhEnumBegin(&xx_pEnum_xx, pDisp, szMember, arg1, arg2, arg3, arg4))) { \ + CDispPtr objName; \ + while (dhEnumNextObject(xx_pEnum_xx, &objName) == NOERROR) { + +#undef ON_FOR_EACH_ERROR +#define ON_FOR_EACH_ERROR(objName) }} else {{ + +#undef NEXT +#define NEXT(objName) }}} + +#define NEXT_THROW(objName) }} else { dhThrowFunctions::throw_string(); }} + +#endif /* DISPHELPER_NO_FOR_EACH */ + +#ifdef _MSC_VER +#pragma warning( default : 4290 ) +#endif + +#endif /* defined(__cplusplus) && !defined(DISPHELPER_NO_CPP_EXTENSIONS) */ + +#endif /* ----- DISPHELPER_H_INCLUDED ----- */