Line |
Branch |
Exec |
Source |
1 |
|
|
/* GATE PROJECT LICENSE: |
2 |
|
|
+----------------------------------------------------------------------------+ |
3 |
|
|
| Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> | |
4 |
|
|
| All rights reserved. | |
5 |
|
|
| | |
6 |
|
|
| Redistribution and use in source and binary forms, with or without | |
7 |
|
|
| modification, are permitted provided that the following conditions are met:| |
8 |
|
|
| | |
9 |
|
|
| 1. Redistributions of source code must retain the above copyright notice, | |
10 |
|
|
| this list of conditions and the following disclaimer. | |
11 |
|
|
| 2. Redistributions in binary form must reproduce the above copyright | |
12 |
|
|
| notice, this list of conditions and the following disclaimer in the | |
13 |
|
|
| documentation and/or other materials provided with the distribution. | |
14 |
|
|
| | |
15 |
|
|
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"| |
16 |
|
|
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 |
|
|
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 |
|
|
| ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
19 |
|
|
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
20 |
|
|
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
21 |
|
|
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
22 |
|
|
| INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
23 |
|
|
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
24 |
|
|
| ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
25 |
|
|
| THE POSSIBILITY OF SUCH DAMAGE. | |
26 |
|
|
+----------------------------------------------------------------------------+ |
27 |
|
|
*/ |
28 |
|
|
|
29 |
|
|
#include "gate/net/winrmclients.h" |
30 |
|
|
#include "gate/results.h" |
31 |
|
|
#include "gate/debugging.h" |
32 |
|
|
#include "gate/uris.h" |
33 |
|
|
#include "gate/net/httpclients.h" |
34 |
|
|
#include "gate/encode/base64.h" |
35 |
|
|
#include "gate/encode/xml.h" |
36 |
|
|
|
37 |
|
|
static gate_string_t const winrm_uri_wmi = GATE_STRING_INIT_STATIC("http://schemas.microsoft.com/wbem/wsman/1/wmi"); |
38 |
|
|
static gate_string_t const winrm_uri_wmicimv2 = GATE_STRING_INIT_STATIC("http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2"); |
39 |
|
|
static gate_string_t const winrm_uri_cimv2 = GATE_STRING_INIT_STATIC("http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2"); |
40 |
|
|
static gate_string_t const winrm_uri_wsman = GATE_STRING_INIT_STATIC("http://schemas.microsoft.com/wbem/wsman/1"); |
41 |
|
|
static gate_string_t const winrm_uri_shell = GATE_STRING_INIT_STATIC("http://schemas.microsoft.com/wbem/wsman/1/windows/shell"); |
42 |
|
|
static gate_string_t const winrm_uri_shell_cmd = GATE_STRING_INIT_STATIC("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd"); |
43 |
|
|
|
44 |
|
|
#define WINRM_TOKEN_TARGET_URL "${WINRM_TARGET}" |
45 |
|
|
#define WINRM_TOKEN_URI_SHELL_CMD "${WINRM_SHELL_CMD}" |
46 |
|
|
#define WINRM_TOKEN_MESSAGE_ID "${WINRM_MESSAGE_ID}" |
47 |
|
|
#define WINRM_TOKEN_WORKDIR "${WINRM_WORKDIR}" |
48 |
|
|
#define WINRM_TOKEN_SHELL_UID "${WINRM_SHELL_UID}" |
49 |
|
|
#define WINRM_TOKEN_SHELL_COMMAND "${WINRM_SHELL_COMMAND}" |
50 |
|
|
#define WINRM_TOKEN_SHELL_ARG "${WINRM_SHELL_ARG}" |
51 |
|
|
#define WINRM_TOKEN_SHELL_ARGS "${WINRM_SHELL_ARGS}" |
52 |
|
|
#define WINRM_TOKEN_COMMAND_UID "${WINRM_COMMAND_UID}" |
53 |
|
|
#define WINRM_TOKEN_COMMAND_INPUT "${WINRM_COMMAND_INPUT}" |
54 |
|
|
#define WINRM_TOKEN_TIMEOUT "${WINRM_TOKEN_TIMEOUT}" |
55 |
|
|
|
56 |
|
|
/* https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/7f4a1f31-47d8-4599-a23b-c3834ffae21f */ |
57 |
|
|
|
58 |
|
✗ |
static gate_bool_t replace_token_raw(gate_strbuilder_t* builder, char const* token, gate_string_t const* value) |
59 |
|
|
{ |
60 |
|
✗ |
gate_size_t const replace_count = gate_strbuilder_replace_str( |
61 |
|
✗ |
builder, token, gate_str_length(token), gate_string_ptr(value, 0), gate_string_length(value), 0, 16); |
62 |
|
✗ |
return replace_count > 0; |
63 |
|
|
} |
64 |
|
|
|
65 |
|
✗ |
static void print_guid(gate_guid_t const* message_id, char* buffer) |
66 |
|
|
{ |
67 |
|
✗ |
if (message_id) |
68 |
|
|
{ |
69 |
|
✗ |
gate_guid_to_string(message_id, buffer); |
70 |
|
|
} |
71 |
|
|
else |
72 |
|
|
{ |
73 |
|
|
gate_guid_t new_id; |
74 |
|
✗ |
gate_guid_generate(&new_id); |
75 |
|
✗ |
gate_guid_to_string(message_id, buffer); |
76 |
|
|
} |
77 |
|
✗ |
} |
78 |
|
|
|
79 |
|
✗ |
static gate_bool_t replace_token_with_guid(gate_strbuilder_t* builder, char const* token, gate_guid_t const* guid) |
80 |
|
|
{ |
81 |
|
|
char guid_text_buffer[128]; |
82 |
|
✗ |
gate_string_t guid_str = GATE_STRING_INIT_EMPTY; |
83 |
|
|
|
84 |
|
✗ |
print_guid(guid, guid_text_buffer); |
85 |
|
✗ |
gate_string_create_static_len(&guid_str, guid_text_buffer, gate_str_length(guid_text_buffer)); |
86 |
|
✗ |
return replace_token_raw(builder, token, &guid_str); |
87 |
|
|
} |
88 |
|
|
|
89 |
|
✗ |
static gate_bool_t xml_encode(gate_string_t const* raw_input, gate_string_t* xml_output) |
90 |
|
|
{ |
91 |
|
|
/* TODO */ |
92 |
|
✗ |
return gate_string_duplicate(xml_output, raw_input) != NULL; |
93 |
|
|
} |
94 |
|
|
|
95 |
|
✗ |
static gate_bool_t replace_token_with_xml_content(gate_strbuilder_t* builder, char const* token, gate_string_t const* value) |
96 |
|
|
{ |
97 |
|
✗ |
gate_bool_t ret = false; |
98 |
|
|
gate_string_t xml; |
99 |
|
✗ |
if (xml_encode(value, &xml)) |
100 |
|
|
{ |
101 |
|
✗ |
ret = replace_token_raw(builder, token, &xml); |
102 |
|
✗ |
gate_string_release(&xml); |
103 |
|
|
} |
104 |
|
✗ |
return ret; |
105 |
|
|
} |
106 |
|
|
|
107 |
|
✗ |
static gate_size_t winrm_print_timeout(gate_uint32_t timeout_ms, char* buffer, gate_size_t buffer_len) |
108 |
|
|
{ |
109 |
|
|
gate_strbuilder_t builder; |
110 |
|
✗ |
const gate_real64_t timeout = (gate_real64_t)timeout_ms / 1000.0; |
111 |
|
|
|
112 |
|
✗ |
gate_strbuilder_create_static(&builder, buffer, buffer_len, 0); |
113 |
|
✗ |
gate_strbuilder_append_cstr(&builder, "PT"); |
114 |
|
✗ |
gate_strbuilder_append_real(&builder, timeout, 0, 3, 0); |
115 |
|
✗ |
gate_strbuilder_append_cstr(&builder, "S"); |
116 |
|
✗ |
return gate_strbuilder_length(&builder); |
117 |
|
|
} |
118 |
|
|
|
119 |
|
✗ |
static gate_bool_t replace_token_with_timeout(gate_strbuilder_t* sb, char const* token, gate_uint32_t timeout_ms) |
120 |
|
|
{ |
121 |
|
|
char buffer[64]; |
122 |
|
✗ |
gate_size_t len = winrm_print_timeout(timeout_ms, buffer, sizeof(buffer)); |
123 |
|
✗ |
gate_string_t content = { buffer, len, NULL }; |
124 |
|
|
|
125 |
|
✗ |
return replace_token_raw(sb, token, &content); |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
|
129 |
|
✗ |
static gate_bool_t extract_token(gate_string_t const* content, |
130 |
|
|
gate_string_t const* begin_marker, gate_string_t const* end_marker, |
131 |
|
|
gate_string_t* extracted_data) |
132 |
|
|
{ |
133 |
|
|
gate_size_t pos_begin; |
134 |
|
|
gate_size_t pos_end; |
135 |
|
|
|
136 |
|
✗ |
pos_begin = gate_string_pos(content, begin_marker, 0); |
137 |
|
✗ |
if (pos_begin == GATE_STR_NPOS) |
138 |
|
|
{ |
139 |
|
✗ |
return false; |
140 |
|
|
} |
141 |
|
✗ |
pos_begin += gate_string_length(begin_marker); |
142 |
|
|
|
143 |
|
✗ |
pos_end = gate_string_pos(content, end_marker, pos_begin); |
144 |
|
✗ |
if (pos_begin == GATE_STR_NPOS) |
145 |
|
|
{ |
146 |
|
✗ |
return false; |
147 |
|
|
} |
148 |
|
|
|
149 |
|
✗ |
return NULL != gate_string_substr(extracted_data, content, pos_begin, pos_end - pos_begin); |
150 |
|
|
} |
151 |
|
|
|
152 |
|
✗ |
static gate_bool_t extract_xml_content(gate_string_t const* content, |
153 |
|
|
gate_string_t const* begin_marker, gate_string_t const* end_marker, |
154 |
|
|
gate_string_t* decoded_data) |
155 |
|
|
{ |
156 |
|
✗ |
gate_bool_t ret = false; |
157 |
|
✗ |
gate_string_t data = GATE_STRING_INIT_EMPTY; |
158 |
|
✗ |
if (extract_token(content, begin_marker, end_marker, &data)) |
159 |
|
|
{ |
160 |
|
|
/* TODO: XML decoding */ |
161 |
|
✗ |
ret = NULL != gate_string_create_copy(decoded_data, &data); |
162 |
|
✗ |
gate_string_release(&data); |
163 |
|
|
} |
164 |
|
✗ |
return ret; |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
|
168 |
|
|
|
169 |
|
|
static char const WINRM_SOAP_MSG_CREATESHELL_REQUEST[] = |
170 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
171 |
|
|
"<s:Header>" |
172 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
173 |
|
|
"<wsman:ResourceURI s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
174 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
175 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action>" |
176 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
177 |
|
|
"<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>" |
178 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />" |
179 |
|
|
"<wsman:OptionSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" |
180 |
|
|
"<wsman:Option Name=\"WINRS_NOPROFILE\">TRUE</wsman:Option>" |
181 |
|
|
"<wsman:Option Name=\"WINRS_CODEPAGE\">437</wsman:Option>" |
182 |
|
|
"</wsman:OptionSet>" |
183 |
|
|
"<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>" |
184 |
|
|
"</s:Header>" |
185 |
|
|
"<s:Body>" |
186 |
|
|
"<rsp:Shell xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">" |
187 |
|
|
/* |
188 |
|
|
"<rsp:Environment>" |
189 |
|
|
"<rsp:Variable Name=\"test\">1</rsp:Variable>" |
190 |
|
|
"</rsp:Environment>" |
191 |
|
|
*/ |
192 |
|
|
WINRM_TOKEN_WORKDIR |
193 |
|
|
"<rsp:InputStreams>stdin</rsp:InputStreams>" |
194 |
|
|
"<rsp:OutputStreams>stdout stderr</rsp:OutputStreams>" |
195 |
|
|
"</rsp:Shell>" |
196 |
|
|
"</s:Body>" |
197 |
|
|
"</s:Envelope>"; |
198 |
|
|
|
199 |
|
✗ |
gate_string_t* winrm_create_shell_request(gate_string_t* output, |
200 |
|
|
gate_string_t const* target_url, |
201 |
|
|
gate_guid_t const* message_id, |
202 |
|
|
gate_string_t const* work_dir) |
203 |
|
|
{ |
204 |
|
✗ |
gate_string_t* ret = NULL; |
205 |
|
|
gate_strbuilder_t sb; |
206 |
|
|
|
207 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
208 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_CREATESHELL_REQUEST, sizeof(WINRM_SOAP_MSG_CREATESHELL_REQUEST) - 1); |
209 |
|
|
|
210 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
211 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
212 |
|
✗ |
if (!gate_string_is_empty(work_dir)) |
213 |
|
|
{ |
214 |
|
✗ |
gate_strbuilder_t wd_builder = GATE_INIT_EMPTY; |
215 |
|
✗ |
gate_string_t wd = GATE_STRING_INIT_EMPTY; |
216 |
|
✗ |
gate_strbuilder_create(&wd_builder, 128); |
217 |
|
✗ |
gate_strbuilder_append_cstr(&wd_builder, "<rsp:WorkingDirectory>"); |
218 |
|
✗ |
gate_strbuilder_append_string(&wd_builder, work_dir); |
219 |
|
✗ |
gate_strbuilder_append_cstr(&wd_builder, "</rsp:WorkingDirectory>"); |
220 |
|
✗ |
gate_strbuilder_to_string(&wd_builder, &wd); |
221 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_WORKDIR, &wd); |
222 |
|
✗ |
gate_strbuilder_release(&wd_builder); |
223 |
|
✗ |
gate_string_release(&wd); |
224 |
|
|
} |
225 |
|
|
|
226 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
227 |
|
✗ |
gate_strbuilder_release(&sb); |
228 |
|
✗ |
return ret; |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
|
232 |
|
|
static char const WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST[] = |
233 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
234 |
|
|
"<s:Header>" |
235 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
236 |
|
|
"<wsman:ResourceURI s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
237 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
238 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command</a:Action>" |
239 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
240 |
|
|
"<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>" |
241 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />" |
242 |
|
|
"<wsman:SelectorSet><wsman:Selector Name=\"ShellId\">" WINRM_TOKEN_SHELL_UID "</wsman:Selector></wsman:SelectorSet>" |
243 |
|
|
"<wsman:OptionSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" |
244 |
|
|
"<wsman:Option Name=\"WINRS_CONSOLEMODE_STDIN\">TRUE</wsman:Option>" |
245 |
|
|
/* "<wsman:Option Name=\"WINRS_SKIP_CMD_SHELL\">FALSE</wsman:Option>" */ |
246 |
|
|
"</wsman:OptionSet>" |
247 |
|
|
"<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>" |
248 |
|
|
"</s:Header>" |
249 |
|
|
"<s:Body>" |
250 |
|
|
"<rsp:CommandLine xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">" |
251 |
|
|
"<rsp:Command>" WINRM_TOKEN_SHELL_COMMAND "</rsp:Command>" |
252 |
|
|
/* "<rsp:Arguments>" WINRM_TOKEN_SHELL_ARG "</rsp:Arguments>" */ |
253 |
|
|
WINRM_TOKEN_SHELL_ARGS |
254 |
|
|
"</rsp:CommandLine>" |
255 |
|
|
"</s:Body>" |
256 |
|
|
"</s:Envelope>"; |
257 |
|
|
|
258 |
|
✗ |
gate_string_t* winrm_shell_execute_request(gate_string_t* output, |
259 |
|
|
gate_string_t const* target_url, |
260 |
|
|
gate_guid_t const* message_id, |
261 |
|
|
gate_string_t const* shell_uid, |
262 |
|
|
gate_string_t const* shell_command, |
263 |
|
|
gate_string_t const* shell_args, gate_size_t shell_args_count) |
264 |
|
|
{ |
265 |
|
✗ |
gate_string_t* ret = NULL; |
266 |
|
|
gate_strbuilder_t sb; |
267 |
|
✗ |
gate_string_t cmd_args = GATE_STRING_INIT_EMPTY; |
268 |
|
|
|
269 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
270 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST, sizeof(WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST) - 1); |
271 |
|
|
|
272 |
|
✗ |
if (shell_args_count > 0) |
273 |
|
|
{ |
274 |
|
|
gate_strbuilder_t args_builder; |
275 |
|
|
gate_size_t ndx; |
276 |
|
✗ |
gate_strbuilder_create(&args_builder, 256); |
277 |
|
✗ |
for (ndx = 0; ndx != shell_args_count; ++ndx) |
278 |
|
|
{ |
279 |
|
✗ |
gate_strbuilder_append_cstr(&args_builder, "<rsp:Arguments>"); |
280 |
|
✗ |
gate_strbuilder_append_string(&args_builder, &shell_args[ndx]); |
281 |
|
✗ |
gate_strbuilder_append_cstr(&args_builder, "</rsp:Arguments>"); |
282 |
|
|
} |
283 |
|
|
|
284 |
|
✗ |
gate_strbuilder_to_string(&args_builder, &cmd_args); |
285 |
|
✗ |
gate_strbuilder_release(&args_builder); |
286 |
|
|
} |
287 |
|
|
|
288 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
289 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
290 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_uid); |
291 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_COMMAND, shell_command); |
292 |
|
✗ |
replace_token_raw(&sb, WINRM_TOKEN_SHELL_ARGS, &cmd_args); |
293 |
|
|
|
294 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
295 |
|
✗ |
gate_strbuilder_release(&sb); |
296 |
|
✗ |
gate_string_release(&cmd_args); |
297 |
|
✗ |
return ret; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
static char const WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST[] = |
301 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
302 |
|
|
"<s:Header>" |
303 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
304 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
305 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action>" |
306 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
307 |
|
|
"<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>" |
308 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />" |
309 |
|
|
"<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
310 |
|
|
"<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
311 |
|
|
"<wsman:Selector Name=\"ShellId\">" WINRM_TOKEN_SHELL_UID "</wsman:Selector>" |
312 |
|
|
"</wsman:SelectorSet>" |
313 |
|
|
"<wsman:OperationTimeout>" WINRM_TOKEN_TIMEOUT "</wsman:OperationTimeout>" |
314 |
|
|
"</s:Header>" |
315 |
|
|
"<s:Body>" |
316 |
|
|
"<rsp:Receive xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" SequenceId=\"0\">" |
317 |
|
|
"<rsp:DesiredStream CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">stdout stderr</rsp:DesiredStream>" |
318 |
|
|
"</rsp:Receive>" |
319 |
|
|
"</s:Body>" |
320 |
|
|
"</s:Envelope>"; |
321 |
|
|
|
322 |
|
|
|
323 |
|
✗ |
gate_string_t* winrm_shell_receiveoutput_request(gate_string_t* output, |
324 |
|
|
gate_string_t const* target_url, |
325 |
|
|
gate_guid_t const* message_id, |
326 |
|
|
gate_string_t const* shell_id, |
327 |
|
|
gate_string_t const* command_id, |
328 |
|
|
gate_uint32_t timeout_ms) |
329 |
|
|
{ |
330 |
|
✗ |
gate_string_t* ret = NULL; |
331 |
|
|
gate_strbuilder_t sb; |
332 |
|
|
|
333 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
334 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST, sizeof(WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST) - 1); |
335 |
|
|
|
336 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
337 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
338 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id); |
339 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id); |
340 |
|
✗ |
replace_token_with_timeout(&sb, WINRM_TOKEN_TIMEOUT, timeout_ms); |
341 |
|
|
|
342 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
343 |
|
✗ |
gate_strbuilder_release(&sb); |
344 |
|
✗ |
return ret; |
345 |
|
|
} |
346 |
|
|
|
347 |
|
|
static char const WINRM_SOAP_MSG_SENDINPUT_REQUEST[] = |
348 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
349 |
|
|
"<s:Header>" |
350 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
351 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
352 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send</a:Action>" |
353 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
354 |
|
|
"<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>" |
355 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />" |
356 |
|
|
"<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
357 |
|
|
"<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
358 |
|
|
"<wsman:Selector Name=\"ShellId\">uuid:" WINRM_TOKEN_SHELL_UID "</wsman:Selector>" |
359 |
|
|
"</wsman:SelectorSet>" |
360 |
|
|
"<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>" |
361 |
|
|
"</s:Header>" |
362 |
|
|
"<s:Body>" |
363 |
|
|
"<rsp:Send xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">" |
364 |
|
|
"<rsp:Stream xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" Name=\"stdin\" CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">" |
365 |
|
|
WINRM_TOKEN_COMMAND_INPUT |
366 |
|
|
"</rsp:Stream>" |
367 |
|
|
"</rsp:Send>" |
368 |
|
|
"</s:Body>" |
369 |
|
|
"</s:Envelope>"; |
370 |
|
|
|
371 |
|
✗ |
gate_string_t* winrm_shell_sendinput_request(gate_string_t* output, |
372 |
|
|
gate_string_t const* target_url, |
373 |
|
|
gate_guid_t const* message_id, |
374 |
|
|
gate_string_t const* shell_id, |
375 |
|
|
gate_string_t const* command_id, |
376 |
|
|
gate_string_t const* input) |
377 |
|
|
{ |
378 |
|
✗ |
gate_string_t* ret = NULL; |
379 |
|
|
gate_strbuilder_t sb; |
380 |
|
|
|
381 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
382 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_SENDINPUT_REQUEST, sizeof(WINRM_SOAP_MSG_SENDINPUT_REQUEST) - 1); |
383 |
|
|
|
384 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
385 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
386 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id); |
387 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id); |
388 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_INPUT, input); |
389 |
|
|
|
390 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
391 |
|
✗ |
gate_strbuilder_release(&sb); |
392 |
|
✗ |
return ret; |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
static char const WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST[] = |
396 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
397 |
|
|
"<s:Header>" |
398 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
399 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
400 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal</a:Action>" |
401 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
402 |
|
|
"<wsa:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</wsa:MessageID>" |
403 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\"/>" |
404 |
|
|
"<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
405 |
|
|
"<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
406 |
|
|
"<wsman:Selector Name=\"ShellId\">uuid:" WINRM_TOKEN_SHELL_UID "</wsman:Selector>" |
407 |
|
|
"</wsman:SelectorSet>" |
408 |
|
|
"<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>" |
409 |
|
|
"</s:Header>" |
410 |
|
|
"<s:Body>" |
411 |
|
|
"<rsp:Signal xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">" |
412 |
|
|
"<rsp:Code>http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/ctrl_c</rsp:Code>" |
413 |
|
|
"</rsp:Signal>" |
414 |
|
|
"</s:Body>" |
415 |
|
|
"</s:Envelope>"; |
416 |
|
|
|
417 |
|
✗ |
gate_string_t* winrm_shell_terminate_request(gate_string_t* output, |
418 |
|
|
gate_string_t const* target_url, |
419 |
|
|
gate_guid_t const* message_id, |
420 |
|
|
gate_string_t const* shell_id, |
421 |
|
|
gate_string_t const* command_id) |
422 |
|
|
{ |
423 |
|
✗ |
gate_string_t* ret = NULL; |
424 |
|
|
gate_strbuilder_t sb; |
425 |
|
|
|
426 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
427 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST, sizeof(WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST) - 1); |
428 |
|
|
|
429 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
430 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
431 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id); |
432 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id); |
433 |
|
|
|
434 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
435 |
|
✗ |
gate_strbuilder_release(&sb); |
436 |
|
✗ |
return ret; |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
static char const WINRM_SOAP_MSG_DELETESHELL_REQUEST[] = |
440 |
|
|
"<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
441 |
|
|
"<s:Header>" |
442 |
|
|
"<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>" |
443 |
|
|
"<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" |
444 |
|
|
"<a:Action s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action>" |
445 |
|
|
"<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>" |
446 |
|
|
"<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>" |
447 |
|
|
"<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />" |
448 |
|
|
"<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>" |
449 |
|
|
"<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">" |
450 |
|
|
"<wsman:Selector Name=\"ShellId\">uuid:" WINRM_TOKEN_SHELL_UID "</wsman:Selector>" |
451 |
|
|
"</wsman:SelectorSet>" |
452 |
|
|
"<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>" |
453 |
|
|
"</s:Header>" |
454 |
|
|
"<s:Body></s:Body>" |
455 |
|
|
"</s:Envelope>"; |
456 |
|
|
|
457 |
|
✗ |
gate_string_t* winrm_delete_shell_request(gate_string_t* output, |
458 |
|
|
gate_string_t const* target_url, |
459 |
|
|
gate_guid_t const* message_id, |
460 |
|
|
gate_string_t const* shell_id) |
461 |
|
|
{ |
462 |
|
✗ |
gate_string_t* ret = NULL; |
463 |
|
|
gate_strbuilder_t sb; |
464 |
|
|
|
465 |
|
✗ |
gate_strbuilder_create(&sb, 2048); |
466 |
|
✗ |
gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_DELETESHELL_REQUEST, sizeof(WINRM_SOAP_MSG_DELETESHELL_REQUEST) - 1); |
467 |
|
|
|
468 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url); |
469 |
|
✗ |
replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id); |
470 |
|
✗ |
replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id); |
471 |
|
|
|
472 |
|
✗ |
ret = gate_strbuilder_to_string(&sb, output); |
473 |
|
✗ |
gate_strbuilder_release(&sb); |
474 |
|
✗ |
return ret; |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
|
478 |
|
✗ |
gate_result_t gate_winrmclient_create(gate_winrmclient_t* client, |
479 |
|
|
gate_string_t const* server, gate_uint16_t port, |
480 |
|
|
gate_string_t const* user, gate_string_t const* password, |
481 |
|
|
gate_enumint_t flags) |
482 |
|
|
{ |
483 |
|
|
static gate_string_t const wsman_path = GATE_STRING_INIT_STATIC("/wsman"); |
484 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
485 |
|
✗ |
gate_bool_t const secure_flag = GATE_FLAG_ENABLED(flags, GATE_WINRMCLIENT_FLAG_SECURE); |
486 |
|
|
|
487 |
|
|
do |
488 |
|
|
{ |
489 |
|
✗ |
if (!client || !server) |
490 |
|
|
{ |
491 |
|
✗ |
ret = GATE_RESULT_INVALIDARG; |
492 |
|
✗ |
break; |
493 |
|
|
} |
494 |
|
|
|
495 |
|
✗ |
gate_mem_clear(client, sizeof(gate_winrmclient_t)); |
496 |
|
|
|
497 |
|
✗ |
ret = gate_uri_init(&client->uri); |
498 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
499 |
|
|
|
500 |
|
✗ |
gate_string_create_static(&client->uri.scheme, secure_flag ? GATE_URI_SCHEME_HTTPS : GATE_URI_SCHEME_HTTP); |
501 |
|
|
|
502 |
|
✗ |
if (NULL == gate_string_clone(&client->uri.host, server)) |
503 |
|
|
{ |
504 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
505 |
|
✗ |
break; |
506 |
|
|
} |
507 |
|
|
|
508 |
|
✗ |
if (NULL == gate_string_clone(&client->uri.absolute_path, &wsman_path)) |
509 |
|
|
{ |
510 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
511 |
|
✗ |
break; |
512 |
|
|
} |
513 |
|
|
|
514 |
|
✗ |
if (!gate_string_is_empty(user) || !gate_string_is_empty(password)) |
515 |
|
|
{ |
516 |
|
✗ |
ret = gate_uri_build_user_info(user, password, &client->uri.user_info); |
517 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
✗ |
client->uri.port = (port == 0) ? (secure_flag ? 5986 : 5985) : port; |
521 |
|
|
|
522 |
|
✗ |
ret = GATE_RESULT_OK; |
523 |
|
|
|
524 |
|
|
} while (0); |
525 |
|
|
|
526 |
|
✗ |
if (client && GATE_FAILED(ret)) |
527 |
|
|
{ |
528 |
|
✗ |
gate_uri_destroy(&client->uri); |
529 |
|
|
} |
530 |
|
✗ |
return ret; |
531 |
|
|
} |
532 |
|
|
|
533 |
|
✗ |
gate_result_t gate_winrmclient_release(gate_winrmclient_t* client) |
534 |
|
|
{ |
535 |
|
|
gate_result_t ret; |
536 |
|
|
|
537 |
|
✗ |
GATE_DEBUG_ASSERT(client != NULL); |
538 |
|
|
do |
539 |
|
|
{ |
540 |
|
✗ |
ret = gate_uri_destroy(&client->uri); |
541 |
|
|
} while (0); |
542 |
|
|
|
543 |
|
✗ |
return ret; |
544 |
|
|
} |
545 |
|
|
|
546 |
|
✗ |
static gate_string_t* winrm_create_target_url(gate_string_t* target_url, gate_winrmclient_t* from_client) |
547 |
|
|
{ |
548 |
|
✗ |
gate_string_t* ret = NULL; |
549 |
|
|
gate_uri_t uri; |
550 |
|
|
|
551 |
|
|
do |
552 |
|
|
{ |
553 |
|
|
gate_result_t result; |
554 |
|
✗ |
gate_mem_clear(&uri, sizeof(uri)); |
555 |
|
|
|
556 |
|
✗ |
result = gate_uri_copy(&uri, &from_client->uri); |
557 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
558 |
|
|
|
559 |
|
✗ |
gate_string_release(&uri.user_info); |
560 |
|
✗ |
gate_string_release(&uri.query); |
561 |
|
|
|
562 |
|
✗ |
result = gate_uri_to_string(&uri, target_url, false); |
563 |
|
✗ |
GATE_BREAK_IF_FAILED(result); |
564 |
|
|
|
565 |
|
✗ |
ret = target_url; |
566 |
|
|
} while (0); |
567 |
|
|
|
568 |
|
✗ |
gate_uri_destroy(&uri); |
569 |
|
✗ |
return ret; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
✗ |
static gate_string_t* winrm_create_shell_cmd_uri(gate_string_t* target_uri) |
573 |
|
|
{ |
574 |
|
✗ |
return gate_string_duplicate(target_uri, &winrm_uri_shell_cmd); |
575 |
|
|
} |
576 |
|
|
|
577 |
|
✗ |
static gate_result_t winrm_send_soap_request(gate_winrmclient_t* client, gate_string_t const* soap_request, gate_uint32_t* ptr_status_code, gate_string_t* ptr_soap_response) |
578 |
|
|
{ |
579 |
|
|
gate_result_t ret; |
580 |
|
✗ |
gate_httpclient_t http = GATE_INIT_EMPTY; |
581 |
|
✗ |
gate_http_request_t req = GATE_INIT_EMPTY; |
582 |
|
✗ |
gate_http_response_t resp = GATE_INIT_EMPTY; |
583 |
|
|
gate_memstream_impl_t upload_impl; |
584 |
|
✗ |
gate_memstream_t* ptr_upload_stream = NULL; |
585 |
|
✗ |
gate_size_t upload_size = gate_string_length(soap_request); |
586 |
|
✗ |
gate_stringstream_t* ss = NULL; |
587 |
|
|
static gate_string_t const http_post = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_POST); |
588 |
|
|
static gate_string_t const header_content_type = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTTYPE); |
589 |
|
|
static gate_string_t const header_content_type_value = GATE_STRING_INIT_STATIC("application/soap+xml;charset=UTF-8"); |
590 |
|
|
|
591 |
|
|
do |
592 |
|
|
{ |
593 |
|
✗ |
ret = gate_httpclient_create(&http, &client->uri.host, client->uri.port, 0); |
594 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
595 |
|
|
|
596 |
|
✗ |
ret = gate_http_request_init_uri(&req, &http_post, &client->uri); |
597 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
598 |
|
|
|
599 |
|
✗ |
req.upload_stream = (gate_stream_t*)gate_memstream_create_static_unmanaged_readonly( |
600 |
|
✗ |
&upload_impl, gate_string_ptr(soap_request, 0), upload_size, upload_size); |
601 |
|
|
|
602 |
|
✗ |
gate_map_add(&req.headers, &header_content_type, &header_content_type_value); |
603 |
|
|
|
604 |
|
✗ |
ret = gate_httpclient_send_request(&http, &req, &resp); |
605 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
606 |
|
|
|
607 |
|
✗ |
ss = gate_stringstream_create(256); |
608 |
|
✗ |
if (ss == NULL) |
609 |
|
|
{ |
610 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
611 |
|
✗ |
break; |
612 |
|
|
} |
613 |
|
|
|
614 |
|
✗ |
if (ptr_status_code) |
615 |
|
|
{ |
616 |
|
✗ |
*ptr_status_code = resp.status_code; |
617 |
|
|
} |
618 |
|
|
|
619 |
|
✗ |
ret = gate_stream_transfer(resp.response_stream, (gate_stream_t*)ss); |
620 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
621 |
|
|
|
622 |
|
✗ |
ret = gate_stringstream_to_string(ss, ptr_soap_response); |
623 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
624 |
|
|
|
625 |
|
|
/* success case */ |
626 |
|
✗ |
ret = GATE_RESULT_OK; |
627 |
|
|
} while (0); |
628 |
|
|
|
629 |
|
✗ |
gate_http_response_release(&resp); |
630 |
|
✗ |
gate_http_request_release(&req); |
631 |
|
✗ |
gate_httpclient_release(&http); |
632 |
|
|
|
633 |
|
✗ |
return ret; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
✗ |
static gate_result_t winrm_prepare_request(gate_winrmclient_t* client, gate_guid_t* ptr_new_msg_id, gate_string_t* ptr_target_url) |
637 |
|
|
{ |
638 |
|
✗ |
gate_result_t ret = gate_guid_generate(ptr_new_msg_id); |
639 |
|
✗ |
if (GATE_SUCCEEDED(ret)) |
640 |
|
|
{ |
641 |
|
✗ |
if (NULL == winrm_create_target_url(ptr_target_url, client)) |
642 |
|
|
{ |
643 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
644 |
|
|
} |
645 |
|
|
} |
646 |
|
✗ |
return ret; |
647 |
|
|
} |
648 |
|
|
|
649 |
|
✗ |
static gate_result_t check_status_ok(gate_uint32_t status_code) |
650 |
|
|
{ |
651 |
|
✗ |
switch (status_code) |
652 |
|
|
{ |
653 |
|
✗ |
case 200: return GATE_RESULT_OK; |
654 |
|
✗ |
default: return GATE_RESULT_FAILED; |
655 |
|
|
} |
656 |
|
|
} |
657 |
|
|
|
658 |
|
✗ |
gate_result_t gate_winrmclient_shell_new(gate_winrmclient_t* client, gate_string_t const* workdir, gate_array_t const* env_vars, gate_string_t* shell_id) |
659 |
|
|
{ |
660 |
|
|
gate_result_t ret; |
661 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
662 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
663 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
664 |
|
✗ |
gate_uint32_t status_code = 0; |
665 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
666 |
|
|
static gate_string_t const response_shell_id_begin = GATE_STRING_INIT_STATIC("ShellId>"); |
667 |
|
|
static gate_string_t const response_shell_id_end = GATE_STRING_INIT_STATIC("</"); |
668 |
|
|
|
669 |
|
|
do |
670 |
|
|
{ |
671 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
672 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
673 |
|
|
|
674 |
|
✗ |
if (NULL == winrm_create_shell_request(&soap_request, &target_url, &msg_id, workdir)) |
675 |
|
|
{ |
676 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
677 |
|
✗ |
break; |
678 |
|
|
} |
679 |
|
|
|
680 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
681 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
682 |
|
|
|
683 |
|
✗ |
ret = check_status_ok(status_code); |
684 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
685 |
|
|
|
686 |
|
✗ |
if (!extract_xml_content(&soap_response, &response_shell_id_begin, &response_shell_id_end, shell_id)) |
687 |
|
|
{ |
688 |
|
✗ |
ret = GATE_RESULT_INVALIDCONTENT; |
689 |
|
✗ |
break; |
690 |
|
|
} |
691 |
|
|
|
692 |
|
✗ |
ret = GATE_RESULT_OK; |
693 |
|
|
} while (0); |
694 |
|
|
|
695 |
|
✗ |
gate_string_release(&soap_response); |
696 |
|
✗ |
gate_string_release(&soap_request); |
697 |
|
✗ |
gate_string_release(&target_url); |
698 |
|
|
|
699 |
|
✗ |
return ret; |
700 |
|
|
} |
701 |
|
|
|
702 |
|
✗ |
gate_result_t gate_winrmclient_shell_delete(gate_winrmclient_t* client, gate_string_t const* shell_id) |
703 |
|
|
{ |
704 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
705 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
706 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
707 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
708 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
709 |
|
✗ |
gate_uint32_t status_code = 0; |
710 |
|
|
|
711 |
|
|
do |
712 |
|
|
{ |
713 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
714 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
715 |
|
|
|
716 |
|
✗ |
if (NULL == winrm_delete_shell_request(&soap_request, &target_url, &msg_id, shell_id)) |
717 |
|
|
{ |
718 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
719 |
|
✗ |
break; |
720 |
|
|
} |
721 |
|
|
|
722 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
723 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
724 |
|
|
|
725 |
|
✗ |
ret = check_status_ok(status_code); |
726 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
727 |
|
|
|
728 |
|
✗ |
ret = GATE_RESULT_OK; |
729 |
|
|
} while (0); |
730 |
|
|
|
731 |
|
✗ |
gate_string_release(&soap_request); |
732 |
|
✗ |
gate_string_release(&soap_response); |
733 |
|
✗ |
gate_string_release(&target_url); |
734 |
|
✗ |
return ret; |
735 |
|
|
} |
736 |
|
|
|
737 |
|
|
|
738 |
|
✗ |
gate_result_t gate_winrmclient_shell_command_start( |
739 |
|
|
gate_winrmclient_t* client, gate_string_t const* shell_id, gate_string_t const* command, gate_array_t const* args, |
740 |
|
|
gate_string_t* command_id) |
741 |
|
|
{ |
742 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
743 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
744 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
745 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
746 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
747 |
|
✗ |
gate_uint32_t status_code = 0; |
748 |
|
|
|
749 |
|
|
do |
750 |
|
|
{ |
751 |
|
|
static gate_string_t const resp_command_id_begin = GATE_STRING_INIT_STATIC("CommandId>"); |
752 |
|
|
static gate_string_t const resp_command_id_end = GATE_STRING_INIT_STATIC("</"); |
753 |
|
|
|
754 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
755 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
756 |
|
|
|
757 |
|
✗ |
if (NULL == winrm_shell_execute_request(&soap_request, &target_url, &msg_id, shell_id, command, NULL, 0)) |
758 |
|
|
{ |
759 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
760 |
|
✗ |
break; |
761 |
|
|
} |
762 |
|
|
|
763 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
764 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
765 |
|
|
|
766 |
|
✗ |
ret = check_status_ok(status_code); |
767 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
768 |
|
|
|
769 |
|
✗ |
if (!extract_xml_content(&soap_response, &resp_command_id_begin, &resp_command_id_end, command_id)) |
770 |
|
|
{ |
771 |
|
✗ |
ret = GATE_RESULT_INVALIDCONTENT; |
772 |
|
✗ |
break; |
773 |
|
|
} |
774 |
|
|
|
775 |
|
✗ |
ret = GATE_RESULT_OK; |
776 |
|
|
} while (0); |
777 |
|
|
|
778 |
|
✗ |
gate_string_release(&soap_request); |
779 |
|
✗ |
gate_string_release(&soap_response); |
780 |
|
✗ |
gate_string_release(&target_url); |
781 |
|
✗ |
return ret; |
782 |
|
|
} |
783 |
|
|
|
784 |
|
|
|
785 |
|
✗ |
gate_result_t gate_winrmclient_shell_command_abort(gate_winrmclient_t* client, |
786 |
|
|
gate_string_t const* shell_id, gate_string_t const* command_id) |
787 |
|
|
{ |
788 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
789 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
790 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
791 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
792 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
793 |
|
✗ |
gate_uint32_t status_code = 0; |
794 |
|
|
|
795 |
|
|
do |
796 |
|
|
{ |
797 |
|
|
static gate_string_t const resp_command_id_begin = GATE_STRING_INIT_STATIC("CommandId>"); |
798 |
|
|
static gate_string_t const resp_command_id_end = GATE_STRING_INIT_STATIC("</"); |
799 |
|
|
|
800 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
801 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
802 |
|
|
|
803 |
|
✗ |
if (NULL == winrm_shell_terminate_request(&soap_request, &target_url, &msg_id, shell_id, command_id)) |
804 |
|
|
{ |
805 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
806 |
|
✗ |
break; |
807 |
|
|
} |
808 |
|
|
|
809 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
810 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
811 |
|
|
|
812 |
|
✗ |
ret = check_status_ok(status_code); |
813 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
814 |
|
|
|
815 |
|
✗ |
ret = GATE_RESULT_OK; |
816 |
|
|
} while (0); |
817 |
|
|
|
818 |
|
✗ |
gate_string_release(&soap_request); |
819 |
|
✗ |
gate_string_release(&soap_response); |
820 |
|
✗ |
gate_string_release(&target_url); |
821 |
|
✗ |
return ret; |
822 |
|
|
} |
823 |
|
|
|
824 |
|
|
|
825 |
|
✗ |
gate_result_t gate_winrmclient_shell_command_send(gate_winrmclient_t* client, |
826 |
|
|
gate_string_t const* shell_id, gate_string_t const* command_id, gate_blob_t const* data_buffer) |
827 |
|
|
{ |
828 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
829 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
830 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
831 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
832 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
833 |
|
✗ |
gate_string_t input_b64 = GATE_STRING_INIT_EMPTY; |
834 |
|
✗ |
gate_uint32_t status_code = 0; |
835 |
|
✗ |
char const* ptr_data = (char const*)gate_blob_data(data_buffer); |
836 |
|
✗ |
gate_size_t data_len = gate_blob_length(data_buffer); |
837 |
|
✗ |
gate_string_t input_raw = { ptr_data, data_len, NULL }; |
838 |
|
✗ |
gate_strbuilder_t input_builder = GATE_INIT_EMPTY; |
839 |
|
|
|
840 |
|
|
do |
841 |
|
|
{ |
842 |
|
|
/* encode input as base64 string */ |
843 |
|
✗ |
gate_strbuilder_create(&input_builder, data_len * 4 / 3 + 3); |
844 |
|
✗ |
gate_base64_encode(&input_raw, &input_builder); |
845 |
|
✗ |
gate_strbuilder_to_string(&input_builder, &input_b64); |
846 |
|
|
|
847 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
848 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
849 |
|
|
|
850 |
|
✗ |
if (NULL == winrm_shell_sendinput_request(&soap_request, &target_url, &msg_id, shell_id, command_id, &input_b64)) |
851 |
|
|
{ |
852 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
853 |
|
✗ |
break; |
854 |
|
|
} |
855 |
|
|
|
856 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
857 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
858 |
|
|
|
859 |
|
✗ |
ret = check_status_ok(status_code); |
860 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
861 |
|
|
|
862 |
|
✗ |
ret = GATE_RESULT_OK; |
863 |
|
|
} while (0); |
864 |
|
|
|
865 |
|
✗ |
gate_strbuilder_release(&input_builder); |
866 |
|
✗ |
gate_string_release(&input_b64); |
867 |
|
✗ |
gate_string_release(&soap_request); |
868 |
|
✗ |
gate_string_release(&soap_response); |
869 |
|
✗ |
gate_string_release(&target_url); |
870 |
|
✗ |
return ret; |
871 |
|
|
|
872 |
|
|
} |
873 |
|
|
|
874 |
|
|
|
875 |
|
✗ |
gate_result_t gate_winrmclient_shell_command_receive(gate_winrmclient_t* client, |
876 |
|
|
gate_string_t const* shell_id, gate_string_t const* command_id, gate_blob_t* data_buffer) |
877 |
|
|
{ |
878 |
|
✗ |
gate_result_t ret = GATE_RESULT_FAILED; |
879 |
|
✗ |
gate_guid_t msg_id = GATE_INIT_EMPTY; |
880 |
|
✗ |
gate_string_t target_url = GATE_STRING_INIT_EMPTY; |
881 |
|
✗ |
gate_string_t soap_request = GATE_STRING_INIT_EMPTY; |
882 |
|
✗ |
gate_string_t soap_response = GATE_STRING_INIT_EMPTY; |
883 |
|
✗ |
gate_uint32_t status_code = 0; |
884 |
|
|
|
885 |
|
|
do |
886 |
|
|
{ |
887 |
|
|
static gate_string_t const resp_command_id_begin = GATE_STRING_INIT_STATIC("CommandId>"); |
888 |
|
|
static gate_string_t const resp_command_id_end = GATE_STRING_INIT_STATIC("</"); |
889 |
|
|
|
890 |
|
✗ |
ret = winrm_prepare_request(client, &msg_id, &target_url); |
891 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
892 |
|
|
|
893 |
|
✗ |
if (NULL == winrm_shell_receiveoutput_request(&soap_request, &target_url, &msg_id, shell_id, command_id, 5000)) |
894 |
|
|
{ |
895 |
|
✗ |
ret = GATE_RESULT_OUTOFMEMORY; |
896 |
|
✗ |
break; |
897 |
|
|
} |
898 |
|
|
|
899 |
|
✗ |
ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response); |
900 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
901 |
|
|
|
902 |
|
✗ |
ret = check_status_ok(status_code); |
903 |
|
✗ |
GATE_BREAK_IF_FAILED(ret); |
904 |
|
|
|
905 |
|
|
/* TODO: SOAP response decoding */ |
906 |
|
|
|
907 |
|
✗ |
gate_blob_create_empty(data_buffer); |
908 |
|
|
|
909 |
|
✗ |
ret = GATE_RESULT_OK; |
910 |
|
|
} while (0); |
911 |
|
|
|
912 |
|
✗ |
gate_string_release(&soap_request); |
913 |
|
✗ |
gate_string_release(&soap_response); |
914 |
|
✗ |
gate_string_release(&target_url); |
915 |
|
✗ |
return ret; |
916 |
|
|
|
917 |
|
|
} |
918 |
|
|
|
919 |
|
|
|