message-func.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /* message-func.c -- Functions for working with SSH messages.
  2. *
  3. * Copyright (C) 2013, 2014 Artyom V. Poptsov <poptsov.artyom@gmail.com>
  4. *
  5. * This file is part of Guile-SSH
  6. *
  7. * Guile-SSH is free software: you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * Guile-SSH is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <config.h>
  21. #include <libguile.h>
  22. #include <libssh/libssh.h>
  23. #include <libssh/server.h>
  24. #include "common.h"
  25. #include "channel-type.h"
  26. #include "message-type.h"
  27. #include "message-func.h"
  28. #include "key-type.h"
  29. #include "error.h"
  30. /* Procedures that are used for replying on requests. */
  31. SCM_DEFINE (guile_ssh_message_reply_default,
  32. "message-reply-default", 1, 0, 0,
  33. (SCM msg),
  34. "\
  35. Reduced version of the reply default that only reply with \
  36. SSH_MSG_UNIMPLEMENTED.\n\
  37. \n\
  38. Return value is undefined.\
  39. ")
  40. #define FUNC_NAME s_guile_ssh_message_reply_default
  41. {
  42. struct message_data *msg_data = _scm_to_message_data (msg);
  43. int res = ssh_message_reply_default (msg_data->message);
  44. if (res != SSH_OK)
  45. guile_ssh_error1 (FUNC_NAME, "Unable to reply", msg);
  46. return SCM_UNDEFINED;
  47. }
  48. #undef FUNC_NAME
  49. SCM_DEFINE (guile_ssh_message_service_reply_success,
  50. "message-service-reply-success", 1, 0, 0,
  51. (SCM msg),
  52. "\
  53. Reply with \"success\" status on the service-request message MSG.\n\
  54. Return value is undefined.\
  55. ")
  56. #define FUNC_NAME s_guile_ssh_message_service_reply_success
  57. {
  58. struct message_data *msg_data = _scm_to_message_data (msg);
  59. int res = ssh_message_service_reply_success (msg_data->message);
  60. if (res != SSH_OK)
  61. guile_ssh_error1 (FUNC_NAME, "Unable to reply", msg);
  62. return SCM_UNDEFINED;
  63. }
  64. #undef FUNC_NAME
  65. SCM_DEFINE (guile_ssh_message_auth_reply_success,
  66. "message-auth-reply-success", 2, 0, 0,
  67. (SCM msg, SCM partial_p),
  68. "\
  69. Reply with \"success\" on the auth-request message MSG.\n\
  70. Return value is undefined.\
  71. ")
  72. #define FUNC_NAME s_guile_ssh_message_auth_reply_success
  73. {
  74. struct message_data *msg_data = _scm_to_message_data (msg);
  75. int c_partial_p = scm_to_bool (partial_p);
  76. int res = ssh_message_auth_reply_success (msg_data->message, c_partial_p);
  77. if (res != SSH_OK)
  78. {
  79. guile_ssh_error1 (FUNC_NAME, "Unable to reply",
  80. scm_list_2 (msg, partial_p));
  81. }
  82. return SCM_UNDEFINED;
  83. }
  84. #undef FUNC_NAME
  85. SCM_DEFINE (guile_ssh_message_auth_reply_public_key_ok,
  86. "message-auth-reply-public-key-ok", 1, 0, 0,
  87. (SCM msg),
  88. "\
  89. Reply OK on the public key auth-request message MSG.\n\
  90. Return value is undefined.\
  91. ")
  92. #define FUNC_NAME s_guile_ssh_message_auth_reply_public_key_ok
  93. {
  94. struct message_data *msg_data = _scm_to_message_data (msg);
  95. int res = ssh_message_auth_reply_pk_ok_simple (msg_data->message);
  96. if (res != SSH_OK)
  97. guile_ssh_error1 (FUNC_NAME, "Unable to reply", msg);
  98. return SCM_UNDEFINED;
  99. }
  100. #undef FUNC_NAME
  101. SCM_DEFINE (guile_ssh_message_channel_request_reply_success,
  102. "message-channel-request-reply-success", 1, 0, 0,
  103. (SCM msg),
  104. "\
  105. TODO: Add description.\n\
  106. Return value is undefined.\
  107. ")
  108. #define FUNC_NAME s_guile_ssh_message_channel_request_reply_success
  109. {
  110. struct message_data *msg_data = _scm_to_message_data (msg);
  111. int res = ssh_message_channel_request_reply_success (msg_data->message);
  112. if (res != SSH_OK)
  113. guile_ssh_error1 (FUNC_NAME, "Unable to reply", msg);
  114. return SCM_UNDEFINED;
  115. }
  116. #undef FUNC_NAME
  117. SCM_DEFINE (guile_ssh_message_channel_request_open_reply_accept,
  118. "message-channel-request-open-reply-accept", 1, 0, 0,
  119. (SCM msg),
  120. "\
  121. Accept open-channel request.\n\
  122. Return a new SSH channel.\
  123. ")
  124. {
  125. struct message_data *msg_data = _scm_to_message_data (msg);
  126. ssh_channel ch;
  127. ch = ssh_message_channel_request_open_reply_accept (msg_data->message);
  128. if (! ch)
  129. return SCM_BOOL_F;
  130. SCM channel = _scm_from_channel_data (ch, msg_data->session,
  131. SCM_RDNG | SCM_WRTNG);
  132. SCM_SET_CELL_TYPE (channel, SCM_CELL_TYPE (channel) | SCM_OPN);
  133. return channel;
  134. }
  135. SCM_DEFINE (gssh_message_global_request_reply_success,
  136. "message-global-request-reply-success", 2, 0, 0,
  137. (SCM msg, SCM bound_port), "")
  138. #define FUNC_NAME s_gssh_message_global_request_reply_success
  139. {
  140. struct message_data *md = _scm_to_message_data (msg);
  141. int res;
  142. SCM_ASSERT (scm_is_unsigned_integer (bound_port, 0, UINT16_MAX), bound_port,
  143. SCM_ARG2, FUNC_NAME);
  144. res = ssh_message_global_request_reply_success (md->message,
  145. scm_to_uint16 (bound_port));
  146. if (res != SSH_OK)
  147. {
  148. guile_ssh_error1 (FUNC_NAME, "Unable to reply",
  149. scm_list_2 (msg, bound_port));
  150. }
  151. return SCM_UNDEFINED;
  152. }
  153. #undef FUNC_NAME
  154. static struct symbol_mapping req_types[] = {
  155. { "request-auth", SSH_REQUEST_AUTH },
  156. { "request-channel-open", SSH_REQUEST_CHANNEL_OPEN },
  157. { "request-channel", SSH_REQUEST_CHANNEL },
  158. { "request-service", SSH_REQUEST_SERVICE },
  159. { "request-global", SSH_REQUEST_GLOBAL },
  160. { NULL, -1 }
  161. };
  162. static struct symbol_mapping req_auth_subtypes[] = {
  163. { "auth-method-unknown", SSH_AUTH_METHOD_UNKNOWN },
  164. { "auth-method-none", SSH_AUTH_METHOD_NONE },
  165. { "auth-method-password", SSH_AUTH_METHOD_PASSWORD },
  166. { "auth-method-publickey", SSH_AUTH_METHOD_PUBLICKEY },
  167. { "auth-method-hostbased", SSH_AUTH_METHOD_HOSTBASED },
  168. { "auth-method-interactive", SSH_AUTH_METHOD_INTERACTIVE },
  169. { NULL, -1 }
  170. };
  171. static struct symbol_mapping req_channel_subtypes[] = {
  172. { "channel-request-unknown", SSH_CHANNEL_REQUEST_UNKNOWN },
  173. { "channel-request-pty", SSH_CHANNEL_REQUEST_PTY },
  174. { "channel-request-exec", SSH_CHANNEL_REQUEST_EXEC },
  175. { "channel-request-shell", SSH_CHANNEL_REQUEST_SHELL },
  176. { "channel-request-env", SSH_CHANNEL_REQUEST_ENV },
  177. { "channel-request-subsystem", SSH_CHANNEL_REQUEST_SUBSYSTEM },
  178. { "channel-request-window-change", SSH_CHANNEL_REQUEST_WINDOW_CHANGE },
  179. { NULL, -1 }
  180. };
  181. static struct symbol_mapping req_channel_open_subtypes[] = {
  182. { "channel-unknown", SSH_CHANNEL_UNKNOWN },
  183. { "channel-session", SSH_CHANNEL_SESSION },
  184. { "channel-direct-tcpip", SSH_CHANNEL_DIRECT_TCPIP },
  185. { "channel-forwarded-tcpip", SSH_CHANNEL_FORWARDED_TCPIP },
  186. { "channel-x11", SSH_CHANNEL_X11 },
  187. { NULL, -1 }
  188. };
  189. static struct symbol_mapping req_global_subtypes[] = {
  190. { "global-request-unknown", SSH_GLOBAL_REQUEST_UNKNOWN },
  191. { "global-request-tcpip-forward", SSH_GLOBAL_REQUEST_TCPIP_FORWARD },
  192. { "global-request-cancel-tcpip-forward", SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD },
  193. { NULL, -1 }
  194. };
  195. static struct symbol_mapping pubkey_state_type[] = {
  196. { "error", SSH_PUBLICKEY_STATE_ERROR },
  197. { "none", SSH_PUBLICKEY_STATE_NONE },
  198. { "valid", SSH_PUBLICKEY_STATE_VALID },
  199. { "wrong", SSH_PUBLICKEY_STATE_WRONG },
  200. { NULL, -1 }
  201. };
  202. /* Get a type of the message MSG as a list. car of the list is type
  203. of the message, cdr is a subtype.
  204. Return #f on error. */
  205. static SCM
  206. _ssh_message_type_to_scm (ssh_message msg)
  207. {
  208. int type = ssh_message_type (msg);
  209. int subtype = ssh_message_subtype (msg);
  210. SCM scm_type = _ssh_const_to_scm (req_types, type);
  211. SCM scm_subtype;
  212. switch (type)
  213. {
  214. case SSH_REQUEST_AUTH:
  215. scm_subtype = _ssh_const_to_scm (req_auth_subtypes, subtype);
  216. return scm_list_2 (scm_type, scm_subtype);
  217. case SSH_REQUEST_CHANNEL_OPEN:
  218. scm_subtype = _ssh_const_to_scm (req_channel_open_subtypes, subtype);
  219. return scm_list_2 (scm_type, scm_subtype);
  220. case SSH_REQUEST_CHANNEL:
  221. scm_subtype = _ssh_const_to_scm (req_channel_subtypes, subtype);
  222. return scm_list_2 (scm_type, scm_subtype);
  223. case SSH_REQUEST_GLOBAL:
  224. scm_subtype = _ssh_const_to_scm (req_global_subtypes, subtype);
  225. return scm_list_2 (scm_type, scm_subtype);
  226. case SSH_REQUEST_SERVICE:
  227. return scm_list_1 (scm_type);
  228. default:
  229. return SCM_BOOL_F;
  230. }
  231. }
  232. SCM_DEFINE (guile_ssh_message_get_type,
  233. "message-get-type", 1, 0, 0,
  234. (SCM msg),
  235. "\
  236. Get type of the message MSG.\
  237. ")
  238. {
  239. struct message_data *message_data = _scm_to_message_data (msg);
  240. return _ssh_message_type_to_scm (message_data->message);
  241. }
  242. /* These procedures return a Scheme vector that represents a SSH
  243. request. The goal is to unify way of working with requests. */
  244. /* <result> = "#(" <user> <WSP> <password> <WSP> <key> ")" */
  245. static SCM
  246. get_auth_req (ssh_message msg, SCM scm_msg) /* FIXME: accept only SCM */
  247. {
  248. SCM result = scm_c_make_vector (4, SCM_UNDEFINED);
  249. const char *user = ssh_message_auth_user (msg);
  250. const char *password = ssh_message_auth_password (msg);
  251. ssh_key public_key = ssh_message_auth_pubkey (msg);
  252. SCM pkey_state;
  253. if (user)
  254. SCM_SIMPLE_VECTOR_SET (result, 0, scm_from_locale_string (user));
  255. else
  256. SCM_SIMPLE_VECTOR_SET (result, 0, SCM_BOOL_F);
  257. if (password)
  258. SCM_SIMPLE_VECTOR_SET (result, 1, scm_from_locale_string (password));
  259. else
  260. SCM_SIMPLE_VECTOR_SET (result, 1, SCM_BOOL_F);
  261. SCM_SIMPLE_VECTOR_SET (result, 2, _scm_from_ssh_key (public_key, scm_msg));
  262. pkey_state = _ssh_const_to_scm (pubkey_state_type,
  263. (int) ssh_message_auth_publickey_state (msg));
  264. SCM_SIMPLE_VECTOR_SET (result, 3, pkey_state);
  265. return result;
  266. }
  267. /* <result> = "#(" <term> <WSP> <width> <WSP> <height> <WSP>
  268. <pxwidth> <WSP> <pxheight> ")" */
  269. static SCM
  270. get_pty_req (ssh_message msg)
  271. {
  272. SCM result = scm_c_make_vector (5, SCM_UNDEFINED);
  273. const char *term = ssh_message_channel_request_pty_term (msg);
  274. int w = ssh_message_channel_request_pty_width (msg);
  275. int h = ssh_message_channel_request_pty_height (msg);
  276. int pxw = ssh_message_channel_request_pty_pxwidth (msg);
  277. int pxh = ssh_message_channel_request_pty_pxheight (msg);
  278. SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (term));
  279. SCM_SIMPLE_VECTOR_SET(result, 1, scm_from_int (w));
  280. SCM_SIMPLE_VECTOR_SET(result, 2, scm_from_int (h));
  281. SCM_SIMPLE_VECTOR_SET(result, 3, scm_from_int (pxw));
  282. SCM_SIMPLE_VECTOR_SET(result, 4, scm_from_int (pxh));
  283. return result;
  284. }
  285. /* <result> = "#(" <name> <WSP> <value> ")" */
  286. static SCM
  287. get_env_req (ssh_message msg)
  288. {
  289. SCM result = scm_c_make_vector (3, SCM_UNDEFINED);
  290. const char *name = ssh_message_channel_request_env_name (msg);
  291. const char *value = ssh_message_channel_request_env_value (msg);
  292. SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (name));
  293. SCM_SIMPLE_VECTOR_SET(result, 1, scm_from_locale_string (value));
  294. return result;
  295. }
  296. /* <result> = "#(" <cmd> ")" */
  297. static SCM
  298. get_exec_req (ssh_message msg)
  299. {
  300. SCM result = scm_c_make_vector (1, SCM_UNDEFINED);
  301. const char *cmd = ssh_message_channel_request_command (msg);
  302. SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (cmd));
  303. return result;
  304. }
  305. /* <result> = "#(" <addr> <WSP> <port> ")" */
  306. static SCM
  307. get_global_req (ssh_message msg)
  308. {
  309. SCM result = scm_c_make_vector (2, SCM_UNDEFINED);
  310. const char *addr = ssh_message_global_request_address (msg);
  311. int port = ssh_message_global_request_port (msg);
  312. SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (addr));
  313. SCM_SIMPLE_VECTOR_SET(result, 1, scm_from_int (port));
  314. return result;
  315. }
  316. /* <result> = "#(" <service-request> ")" */
  317. static SCM
  318. get_service_req (ssh_message msg)
  319. {
  320. SCM result = scm_c_make_vector (1, SCM_UNDEFINED);
  321. const char *req = ssh_message_service_service (msg);
  322. SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (req));
  323. return result;
  324. }
  325. /* <result> = "#(" <orig> <WSP> <orig-port> <WSP>
  326. <dest> <WSP> <dest-port> ")" */
  327. static SCM
  328. get_channel_open_req (ssh_message msg)
  329. {
  330. const char *orig = ssh_message_channel_request_open_originator (msg);
  331. int orig_port = ssh_message_channel_request_open_originator_port (msg);
  332. const char *dest = ssh_message_channel_request_open_destination (msg);
  333. int dest_port = ssh_message_channel_request_open_destination_port (msg);
  334. SCM result;
  335. if ((! orig) || (! dest))
  336. return SCM_BOOL_F;
  337. result = scm_c_make_vector (4, SCM_UNDEFINED);
  338. SCM_SIMPLE_VECTOR_SET (result, 0, scm_from_locale_string (orig));
  339. SCM_SIMPLE_VECTOR_SET (result, 1, scm_from_int (orig_port));
  340. SCM_SIMPLE_VECTOR_SET (result, 2, scm_from_locale_string (dest));
  341. SCM_SIMPLE_VECTOR_SET (result, 3, scm_from_int (dest_port));
  342. return result;
  343. }
  344. static SCM
  345. get_subsystem_req (ssh_message msg)
  346. {
  347. const char* subsystem = ssh_message_channel_request_subsystem (msg);
  348. SCM result = SCM_BOOL_F;
  349. if (subsystem)
  350. {
  351. result = scm_c_make_vector (1, SCM_UNDEFINED);
  352. SCM_SIMPLE_VECTOR_SET (result, 0, scm_from_locale_string (subsystem));
  353. }
  354. return result;
  355. }
  356. SCM_DEFINE (guile_ssh_message_get_req,
  357. "message-get-req", 1, 0, 0,
  358. (SCM msg),
  359. "\
  360. Get a request object from the message MSG\
  361. ")
  362. #define FUNC_NAME s_guile_ssh_message_get_req
  363. {
  364. struct message_data *message_data = _scm_to_message_data (msg);
  365. ssh_message ssh_msg = message_data->message;
  366. int type = ssh_message_type (ssh_msg);
  367. switch (type)
  368. {
  369. case SSH_REQUEST_SERVICE:
  370. return get_service_req (ssh_msg);
  371. case SSH_REQUEST_AUTH:
  372. return get_auth_req (ssh_msg, msg);
  373. case SSH_REQUEST_CHANNEL_OPEN:
  374. {
  375. SCM res = get_channel_open_req (ssh_msg);
  376. if (scm_is_true (res))
  377. return res;
  378. else
  379. guile_ssh_error1 (FUNC_NAME, "Wrong channel-open request", msg);
  380. }
  381. case SSH_REQUEST_CHANNEL:
  382. {
  383. int subtype = ssh_message_subtype (ssh_msg);
  384. switch (subtype)
  385. {
  386. case SSH_CHANNEL_REQUEST_PTY:
  387. return get_pty_req (ssh_msg);
  388. case SSH_CHANNEL_REQUEST_EXEC:
  389. return get_exec_req (ssh_msg);
  390. case SSH_CHANNEL_REQUEST_ENV:
  391. return get_env_req (ssh_msg);
  392. case SSH_CHANNEL_REQUEST_SUBSYSTEM:
  393. return get_subsystem_req (ssh_msg);
  394. default:
  395. guile_ssh_error1 (FUNC_NAME, "Wrong message subtype",
  396. scm_from_int (subtype));
  397. }
  398. }
  399. case SSH_REQUEST_GLOBAL:
  400. return get_global_req (ssh_msg);
  401. default:
  402. guile_ssh_error1 (FUNC_NAME, "Wrong message type",
  403. _ssh_const_to_scm (req_types, type));
  404. }
  405. return SCM_BOOL_F; /* Never reached. */
  406. }
  407. #undef FUNC_NAME
  408. /* A convenient wrapper for `scm_member' that returns its result as
  409. int. */
  410. static inline int
  411. _scm_member_p (SCM elem, SCM lst)
  412. {
  413. return scm_is_true (scm_member (elem, lst));
  414. }
  415. SCM_DEFINE (guile_ssh_message_auth_set_methods_x,
  416. "message-auth-set-methods!", 2, 0, 0,
  417. (SCM msg, SCM methods_list),
  418. "\
  419. Set authentication methods.\n\
  420. Return value is undefined.\
  421. ")
  422. #define FUNC_NAME s_guile_ssh_message_auth_set_methods_x
  423. {
  424. struct message_data *message_data = _scm_to_message_data (msg);
  425. int methods = 0;
  426. int res;
  427. SCM_ASSERT (scm_list_p (methods_list), methods_list, SCM_ARG2, FUNC_NAME);
  428. if (_scm_member_p (scm_from_locale_symbol ("password"), methods_list))
  429. methods |= SSH_AUTH_METHOD_PASSWORD;
  430. if (_scm_member_p (scm_from_locale_symbol ("public-key"), methods_list))
  431. methods |= SSH_AUTH_METHOD_PUBLICKEY;
  432. if (_scm_member_p (scm_from_locale_symbol ("interactive"), methods_list))
  433. methods |= SSH_AUTH_METHOD_INTERACTIVE;
  434. if (_scm_member_p (scm_from_locale_symbol ("host-based"), methods_list))
  435. methods |= SSH_AUTH_METHOD_HOSTBASED;
  436. res = ssh_message_auth_set_methods (message_data->message, methods);
  437. if (res != SSH_OK)
  438. {
  439. guile_ssh_error1 (FUNC_NAME, "Unable to set auth methods",
  440. scm_list_2 (msg, methods_list));
  441. }
  442. return SCM_UNDEFINED;
  443. }
  444. #undef FUNC_NAME
  445. SCM_DEFINE (guile_ssh_message_get_session,
  446. "message-get-session", 1, 0, 0,
  447. (SCM message),
  448. "\
  449. Get the session from which the MESSAGE was received. Return the session.\
  450. ")
  451. {
  452. struct message_data *md = _scm_to_message_data (message);
  453. return md->session;
  454. }
  455. void
  456. init_message_func (void)
  457. {
  458. #include "message-func.x"
  459. }
  460. /* message-func.c ends here */