smb_spnego.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*****************************************************************************
  2. * __________________ _________ _____ _____ .__ ._.
  3. * \______ \______ \ / _____/ / \ / _ \ |__| ____ | |
  4. * | | _/| | \ \_____ \ / \ / \ / /_\ \| _/ __ \ | |
  5. * | | \| ` \/ / Y \ / | | \ ___/ \|
  6. * |______ /_______ /_______ \____|__ / /\ \____|__ |__|\___ | __
  7. * \/ \/ \/ \/ )/ \/ \/ \/
  8. *
  9. * This file is part of liBDSM. Copyright © 2014-2015 VideoLabs SAS
  10. *
  11. * Author: Julien 'Lta' BALLET <contact@lta.io>
  12. *
  13. * liBDSM is released under LGPLv2.1 (or later) and is also available
  14. * under a commercial license.
  15. *****************************************************************************
  16. * This program is free software; you can redistribute it and/or modify it
  17. * under the terms of the GNU Lesser General Public License as published by
  18. * the Free Software Foundation; either version 2.1 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Lesser General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU Lesser General Public License
  27. * along with this program; if not, write to the Free Software Foundation,
  28. * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  29. *****************************************************************************/
  30. #ifdef HAVE_CONFIG_H
  31. # include "config.h"
  32. #endif
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <stdio.h>
  36. #include <assert.h>
  37. #include <inttypes.h>
  38. #include "bdsm_debug.h"
  39. #include "smb_session.h"
  40. #include "smb_session_msg.h"
  41. #include "smb_message.h"
  42. #include "smb_ntlm.h"
  43. #include "spnego/spnego_asn1.h"
  44. static const char spnego_oid[] = "1.3.6.1.5.5.2";
  45. static const char ntlmssp_oid[] = "1.3.6.1.4.1.311.2.2.10";
  46. static void asn1_display_error(const char *where, int errcode)
  47. {
  48. // Avoids warning when not in debug mode
  49. (void)where;
  50. (void)errcode;
  51. BDSM_dbg("%s error: %s\n", where, asn1_strerror(errcode));
  52. }
  53. static int init_asn1(smb_session *s)
  54. {
  55. int res;
  56. assert(s != NULL);
  57. if (s->spnego_asn1 != NULL)
  58. return DSM_ERROR_GENERIC;
  59. res = asn1_array2tree(spnego_asn1_conf, &s->spnego_asn1, NULL);
  60. if (res != ASN1_SUCCESS)
  61. {
  62. asn1_display_error("init_asn1", res);
  63. return DSM_ERROR_GENERIC;
  64. }
  65. else
  66. {
  67. BDSM_dbg("init_asn1: ASN.1 parser initialized\n");
  68. return DSM_SUCCESS;
  69. }
  70. }
  71. static void clean_asn1(smb_session *s)
  72. {
  73. assert(s != NULL);
  74. if (s->spnego_asn1 != NULL)
  75. asn1_delete_structure(&s->spnego_asn1);
  76. }
  77. static int negotiate(smb_session *s, const char *domain)
  78. {
  79. smb_message *msg = NULL;
  80. smb_session_xsec_req req;
  81. smb_buffer ntlm;
  82. ASN1_TYPE token;
  83. int res, der_size = 128;
  84. char der[128], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
  85. msg = smb_message_new(SMB_CMD_SETUP);
  86. if (!msg)
  87. return DSM_ERROR_GENERIC;
  88. // this struct will be set at the end when we know the payload size
  89. SMB_MSG_ADVANCE_PKT(msg, smb_session_xsec_req);
  90. asn1_create_element(s->spnego_asn1, "SPNEGO.GSSAPIContextToken", &token);
  91. res = asn1_write_value(token, "thisMech", spnego_oid, 1);
  92. if (res != ASN1_SUCCESS) goto error;
  93. res = asn1_write_value(token, "spnego", "negTokenInit", 1);
  94. if (res != ASN1_SUCCESS) goto error;
  95. res = asn1_write_value(token, "spnego.negTokenInit.mechTypes", "NEW", 1);
  96. if (res != ASN1_SUCCESS) goto error;
  97. res = asn1_write_value(token, "spnego.negTokenInit.mechTypes.?1", ntlmssp_oid, 1);
  98. if (res != ASN1_SUCCESS) goto error;
  99. res = asn1_write_value(token, "spnego.negTokenInit.reqFlags", NULL, 0);
  100. if (res != ASN1_SUCCESS) goto error;
  101. res = asn1_write_value(token, "spnego.negTokenInit.mechListMIC", NULL, 0);
  102. if (res != ASN1_SUCCESS) goto error;
  103. smb_ntlmssp_negotiate(domain, domain, &ntlm);
  104. res = asn1_write_value(token, "spnego.negTokenInit.mechToken", ntlm.data,
  105. ntlm.size);
  106. smb_buffer_free(&ntlm);
  107. if (res != ASN1_SUCCESS) goto error;
  108. res = asn1_der_coding(token, "", der, &der_size, err_desc);
  109. if (res != ASN1_SUCCESS)
  110. {
  111. smb_message_destroy(msg);
  112. BDSM_dbg("Encoding error: %s", err_desc);
  113. return DSM_ERROR_GENERIC;
  114. }
  115. smb_message_append(msg, der, der_size);
  116. smb_message_put_utf16(msg, SMB_OS, strlen(SMB_OS));
  117. smb_message_put16(msg, 0);
  118. smb_message_put_utf16(msg, SMB_LANMAN, strlen(SMB_LANMAN));
  119. smb_message_put16(msg, 0);
  120. smb_message_put16(msg, 0);
  121. SMB_MSG_INIT_PKT_ANDX(req);
  122. req.wct = 12;
  123. req.max_buffer = SMB_SESSION_MAX_BUFFER;
  124. req.mpx_count = 16;
  125. req.vc_count = 1;
  126. req.caps = s->srv.caps;
  127. req.session_key = s->srv.session_key;
  128. req.xsec_blob_size = der_size;
  129. req.payload_size = msg->cursor - sizeof(smb_session_xsec_req);
  130. SMB_MSG_INSERT_PKT(msg, 0, req);
  131. asn1_delete_structure(&token);
  132. if (!smb_session_send_msg(s, msg))
  133. {
  134. smb_message_destroy(msg);
  135. BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_NEGOTIATE) message\n");
  136. return DSM_ERROR_NETWORK;
  137. }
  138. smb_message_destroy(msg);
  139. return DSM_SUCCESS;
  140. error:
  141. asn1_display_error("smb_session_login negotiate()", res);
  142. smb_message_destroy(msg);
  143. return DSM_ERROR_GENERIC;
  144. }
  145. static int challenge(smb_session *s)
  146. {
  147. char err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
  148. char resp_token[256];
  149. smb_message msg;
  150. smb_session_xsec_resp *resp;
  151. smb_ntlmssp_challenge *challenge;
  152. ASN1_TYPE token;
  153. int res, resp_token_size = 256;
  154. assert(s != NULL);
  155. if (smb_session_recv_msg(s, &msg) == 0)
  156. {
  157. BDSM_dbg("spnego challenge(): Unable to receive message\n");
  158. return DSM_ERROR_NETWORK;
  159. }
  160. if (msg.packet->header.status != NT_STATUS_MORE_PROCESSING_REQUIRED)
  161. {
  162. BDSM_dbg("spnego challenge(): Bad status (0x%x)\n",
  163. msg.packet->header.status);
  164. return DSM_ERROR_GENERIC;
  165. }
  166. resp = (smb_session_xsec_resp *)msg.packet->payload;
  167. asn1_create_element(s->spnego_asn1, "SPNEGO.NegotiationToken", &token);
  168. res = asn1_der_decoding(&token, resp->payload, resp->xsec_blob_size,
  169. err_desc);
  170. if (res != ASN1_SUCCESS)
  171. {
  172. asn1_delete_structure(&token);
  173. asn1_display_error("NegTokenResp parsing", res);
  174. BDSM_dbg("Parsing error detail: %s\n", err_desc);
  175. return DSM_ERROR_GENERIC;
  176. }
  177. // XXX Check the value of "negTokenResp.negResult"
  178. res = asn1_read_value(token, "negTokenResp.responseToken", resp_token,
  179. &resp_token_size);
  180. asn1_delete_structure(&token);
  181. if (res != ASN1_SUCCESS)
  182. {
  183. asn1_display_error("NegTokenResp read responseToken", res);
  184. return DSM_ERROR_GENERIC;
  185. }
  186. // We got the server challenge, yeaaah.
  187. challenge = (smb_ntlmssp_challenge *)resp_token;
  188. if (smb_buffer_alloc(&s->xsec_target, challenge->tgt_len) == 0)
  189. return DSM_ERROR_GENERIC;
  190. memcpy(s->xsec_target.data,
  191. challenge->data + challenge->tgt_offset - sizeof(smb_ntlmssp_challenge),
  192. s->xsec_target.size);
  193. s->srv.challenge = challenge->challenge;
  194. s->srv.uid = msg.packet->header.uid;
  195. BDSM_dbg("Server challenge is 0x%"PRIx64"\n", s->srv.challenge);
  196. return DSM_SUCCESS;
  197. }
  198. static int auth(smb_session *s, const char *domain, const char *user,
  199. const char *password)
  200. {
  201. smb_message *msg = NULL, resp;
  202. smb_session_xsec_req req;
  203. smb_buffer ntlm;
  204. ASN1_TYPE token;
  205. int res, der_size = 512;
  206. char der[512], err_desc[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
  207. msg = smb_message_new(SMB_CMD_SETUP);
  208. if (!msg)
  209. return DSM_ERROR_GENERIC;
  210. // this struct will be set at the end when we know the payload size
  211. SMB_MSG_ADVANCE_PKT(msg, smb_session_xsec_req);
  212. asn1_create_element(s->spnego_asn1, "SPNEGO.NegotiationToken", &token);
  213. // Select a response message type
  214. res = asn1_write_value(token, "", "negTokenResp", 1);
  215. if (res != ASN1_SUCCESS) goto error;
  216. // Delete all optionnal field except 'ResponseToken'
  217. res = asn1_write_value(token, "negTokenResp.negResult", NULL, 0);
  218. if (res != ASN1_SUCCESS) goto error;
  219. res = asn1_write_value(token, "negTokenResp.supportedMech", NULL, 0);
  220. if (res != ASN1_SUCCESS) goto error;
  221. res = asn1_write_value(token, "negTokenResp.mechListMIC", NULL, 0);
  222. if (res != ASN1_SUCCESS) goto error;
  223. smb_ntlmssp_response(s->srv.challenge, s->srv.ts - 4200, domain, domain, user,
  224. password, &s->xsec_target, &ntlm);
  225. res = asn1_write_value(token, "negTokenResp.responseToken", ntlm.data,
  226. ntlm.size);
  227. smb_buffer_free(&ntlm);
  228. if (res != ASN1_SUCCESS) goto error;
  229. res = asn1_der_coding(token, "", der, &der_size, err_desc);
  230. if (res != ASN1_SUCCESS)
  231. {
  232. smb_message_destroy(msg);
  233. BDSM_dbg("Encoding error: %s", err_desc);
  234. return DSM_ERROR_GENERIC;
  235. }
  236. smb_message_append(msg, der, der_size);
  237. if (msg->cursor % 2)
  238. smb_message_put8(msg, 0);
  239. smb_message_put_utf16(msg, SMB_OS, strlen(SMB_OS));
  240. smb_message_put16(msg, 0);
  241. smb_message_put_utf16(msg, SMB_LANMAN, strlen(SMB_LANMAN));
  242. smb_message_put16(msg, 0);
  243. smb_message_put16(msg, 0); // Empty PDC name
  244. SMB_MSG_INIT_PKT_ANDX(req);
  245. req.wct = 12;
  246. req.max_buffer = SMB_SESSION_MAX_BUFFER;
  247. req.mpx_count = 16; // XXX ?
  248. req.vc_count = 1;
  249. req.caps = s->srv.caps; // XXX caps & our_caps_mask
  250. req.session_key = s->srv.session_key;
  251. req.xsec_blob_size = der_size;
  252. req.payload_size = msg->cursor - sizeof(smb_session_xsec_req);
  253. SMB_MSG_INSERT_PKT(msg, 0, req);
  254. asn1_delete_structure(&token);
  255. if (!smb_session_send_msg(s, msg))
  256. {
  257. smb_message_destroy(msg);
  258. BDSM_dbg("Unable to send Session Setup AndX (NTLMSSP_AUTH) message\n");
  259. return DSM_ERROR_NETWORK;
  260. }
  261. smb_message_destroy(msg);
  262. if (smb_session_recv_msg(s, &resp) == 0)
  263. return DSM_ERROR_NETWORK;
  264. if (!smb_session_check_nt_status(s, &resp))
  265. return DSM_ERROR_NT;
  266. else
  267. {
  268. smb_session_xsec_resp *r = (smb_session_xsec_resp *)resp.packet->payload;
  269. if (r->action & 0x0001)
  270. s->guest = true;
  271. s->srv.uid = resp.packet->header.uid;
  272. s->logged = true;
  273. return DSM_SUCCESS;
  274. }
  275. error:
  276. asn1_display_error("smb_session_login auth()", res);
  277. smb_message_destroy(msg);
  278. return DSM_ERROR_GENERIC;
  279. }
  280. int smb_session_login_spnego(smb_session *s, const char *domain,
  281. const char *user, const char *password)
  282. {
  283. int res;
  284. assert(s != NULL && domain != NULL && user != NULL && password != NULL);
  285. // Clear User ID that might exists from previous authentication attempt
  286. s->srv.uid = 0;
  287. if (init_asn1(s) != DSM_SUCCESS)
  288. return DSM_ERROR_GENERIC;
  289. if ((res = negotiate(s, domain)) != DSM_SUCCESS)
  290. goto error;
  291. if ((res = challenge(s)) != DSM_SUCCESS)
  292. goto error;
  293. res = auth(s, domain, user, password);
  294. clean_asn1(s);
  295. return res;
  296. error:
  297. BDSM_dbg("login_spnego Interrupted\n");
  298. clean_asn1(s);
  299. return res;
  300. }