security_context.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. #include <assert.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <node.h>
  5. #include <v8.h>
  6. #include <node_buffer.h>
  7. #include <cstring>
  8. #include <cmath>
  9. #include <cstdlib>
  10. #include <iostream>
  11. #include <limits>
  12. #include "security_context.h"
  13. #include "security_buffer_descriptor.h"
  14. #ifndef ARRAY_SIZE
  15. # define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
  16. #endif
  17. static LPSTR DisplaySECError(DWORD ErrCode);
  18. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  19. // UV Lib callbacks
  20. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  21. static void Process(uv_work_t* work_req) {
  22. // Grab the worker
  23. Worker *worker = static_cast<Worker*>(work_req->data);
  24. // Execute the worker code
  25. worker->execute(worker);
  26. }
  27. static void After(uv_work_t* work_req) {
  28. // Grab the scope of the call from Node
  29. Nan::HandleScope scope;
  30. // Get the worker reference
  31. Worker *worker = static_cast<Worker*>(work_req->data);
  32. // If we have an error
  33. if(worker->error) {
  34. Local<Value> err = v8::Exception::Error(Nan::New<String>(worker->error_message).ToLocalChecked());
  35. Local<Object> obj = err->ToObject();
  36. obj->Set(Nan::New<String>("code").ToLocalChecked(), Nan::New<Int32>(worker->error_code));
  37. Local<Value> info[2] = { err, Nan::Null() };
  38. // Execute the error
  39. Nan::TryCatch try_catch;
  40. // Call the callback
  41. worker->callback->Call(ARRAY_SIZE(info), info);
  42. // If we have an exception handle it as a fatalexception
  43. if (try_catch.HasCaught()) {
  44. Nan::FatalException(try_catch);
  45. }
  46. } else {
  47. // // Map the data
  48. Local<Value> result = worker->mapper(worker);
  49. // Set up the callback with a null first
  50. Local<Value> info[2] = { Nan::Null(), result};
  51. // Wrap the callback function call in a TryCatch so that we can call
  52. // node's FatalException afterwards. This makes it possible to catch
  53. // the exception from JavaScript land using the
  54. // process.on('uncaughtException') event.
  55. Nan::TryCatch try_catch;
  56. // Call the callback
  57. worker->callback->Call(ARRAY_SIZE(info), info);
  58. // If we have an exception handle it as a fatalexception
  59. if (try_catch.HasCaught()) {
  60. Nan::FatalException(try_catch);
  61. }
  62. }
  63. // Clean up the memory
  64. delete worker->callback;
  65. delete worker;
  66. }
  67. Nan::Persistent<FunctionTemplate> SecurityContext::constructor_template;
  68. SecurityContext::SecurityContext() : Nan::ObjectWrap() {
  69. }
  70. SecurityContext::~SecurityContext() {
  71. if(this->hasContext) {
  72. _sspi_DeleteSecurityContext(&this->m_Context);
  73. }
  74. }
  75. NAN_METHOD(SecurityContext::New) {
  76. PSecurityFunctionTable pSecurityInterface = NULL;
  77. DWORD dwNumOfPkgs;
  78. SECURITY_STATUS status;
  79. // Create code object
  80. SecurityContext *security_obj = new SecurityContext();
  81. // Get security table interface
  82. pSecurityInterface = _ssip_InitSecurityInterface();
  83. // Call the security interface
  84. status = (*pSecurityInterface->EnumerateSecurityPackages)(
  85. &dwNumOfPkgs,
  86. &security_obj->m_PkgInfo);
  87. if(status != SEC_E_OK) {
  88. printf(TEXT("Failed in retrieving security packages, Error: %x"), GetLastError());
  89. return Nan::ThrowError("Failed in retrieving security packages");
  90. }
  91. // Wrap it
  92. security_obj->Wrap(info.This());
  93. // Return the object
  94. info.GetReturnValue().Set(info.This());
  95. }
  96. //
  97. // Async InitializeContext
  98. //
  99. typedef struct SecurityContextStaticInitializeCall {
  100. char *service_principal_name_str;
  101. char *decoded_input_str;
  102. int decoded_input_str_length;
  103. SecurityContext *context;
  104. } SecurityContextStaticInitializeCall;
  105. static void _initializeContext(Worker *worker) {
  106. // Status of operation
  107. SECURITY_STATUS status;
  108. BYTE *out_bound_data_str = NULL;
  109. SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)worker->parameters;
  110. // Structures used for c calls
  111. SecBufferDesc ibd, obd;
  112. SecBuffer ib, ob;
  113. //
  114. // Prepare data structure for returned data from SSPI
  115. ob.BufferType = SECBUFFER_TOKEN;
  116. ob.cbBuffer = call->context->m_PkgInfo->cbMaxToken;
  117. // Allocate space for return data
  118. out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
  119. ob.pvBuffer = out_bound_data_str;
  120. // prepare buffer description
  121. obd.cBuffers = 1;
  122. obd.ulVersion = SECBUFFER_VERSION;
  123. obd.pBuffers = &ob;
  124. //
  125. // Prepare the data we are passing to the SSPI method
  126. if(call->decoded_input_str_length > 0) {
  127. ib.BufferType = SECBUFFER_TOKEN;
  128. ib.cbBuffer = call->decoded_input_str_length;
  129. ib.pvBuffer = call->decoded_input_str;
  130. // prepare buffer description
  131. ibd.cBuffers = 1;
  132. ibd.ulVersion = SECBUFFER_VERSION;
  133. ibd.pBuffers = &ib;
  134. }
  135. // Perform initialization step
  136. status = _sspi_initializeSecurityContext(
  137. &call->context->security_credentials->m_Credentials
  138. , NULL
  139. , const_cast<TCHAR*>(call->service_principal_name_str)
  140. , 0x02 // MUTUAL
  141. , 0
  142. , 0 // Network
  143. , call->decoded_input_str_length > 0 ? &ibd : NULL
  144. , 0
  145. , &call->context->m_Context
  146. , &obd
  147. , &call->context->CtxtAttr
  148. , &call->context->Expiration
  149. );
  150. // If we have a ok or continue let's prepare the result
  151. if(status == SEC_E_OK
  152. || status == SEC_I_COMPLETE_NEEDED
  153. || status == SEC_I_CONTINUE_NEEDED
  154. || status == SEC_I_COMPLETE_AND_CONTINUE
  155. ) {
  156. call->context->hasContext = true;
  157. call->context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
  158. // Set the context
  159. worker->return_code = status;
  160. worker->return_value = call->context;
  161. } else if(status == SEC_E_INSUFFICIENT_MEMORY) {
  162. worker->error = TRUE;
  163. worker->error_code = status;
  164. worker->error_message = "SEC_E_INSUFFICIENT_MEMORY There is not enough memory available to complete the requested action.";
  165. } else if(status == SEC_E_INTERNAL_ERROR) {
  166. worker->error = TRUE;
  167. worker->error_code = status;
  168. worker->error_message = "SEC_E_INTERNAL_ERROR An error occurred that did not map to an SSPI error code.";
  169. } else if(status == SEC_E_INVALID_HANDLE) {
  170. worker->error = TRUE;
  171. worker->error_code = status;
  172. worker->error_message = "SEC_E_INVALID_HANDLE The handle passed to the function is not valid.";
  173. } else if(status == SEC_E_INVALID_TOKEN) {
  174. worker->error = TRUE;
  175. worker->error_code = status;
  176. worker->error_message = "SEC_E_INVALID_TOKEN The error is due to a malformed input token, such as a token corrupted in transit, a token of incorrect size, or a token passed into the wrong security package. Passing a token to the wrong package can happen if the client and server did not negotiate the proper security package.";
  177. } else if(status == SEC_E_LOGON_DENIED) {
  178. worker->error = TRUE;
  179. worker->error_code = status;
  180. worker->error_message = "SEC_E_LOGON_DENIED The logon failed.";
  181. } else if(status == SEC_E_NO_AUTHENTICATING_AUTHORITY) {
  182. worker->error = TRUE;
  183. worker->error_code = status;
  184. worker->error_message = "SEC_E_NO_AUTHENTICATING_AUTHORITY No authority could be contacted for authentication. The domain name of the authenticating party could be wrong, the domain could be unreachable, or there might have been a trust relationship failure.";
  185. } else if(status == SEC_E_NO_CREDENTIALS) {
  186. worker->error = TRUE;
  187. worker->error_code = status;
  188. worker->error_message = "SEC_E_NO_CREDENTIALS No credentials are available in the security package.";
  189. } else if(status == SEC_E_TARGET_UNKNOWN) {
  190. worker->error = TRUE;
  191. worker->error_code = status;
  192. worker->error_message = "SEC_E_TARGET_UNKNOWN The target was not recognized.";
  193. } else if(status == SEC_E_UNSUPPORTED_FUNCTION) {
  194. worker->error = TRUE;
  195. worker->error_code = status;
  196. worker->error_message = "SEC_E_UNSUPPORTED_FUNCTION A context attribute flag that is not valid (ISC_REQ_DELEGATE or ISC_REQ_PROMPT_FOR_CREDS) was specified in the fContextReq parameter.";
  197. } else if(status == SEC_E_WRONG_PRINCIPAL) {
  198. worker->error = TRUE;
  199. worker->error_code = status;
  200. worker->error_message = "SEC_E_WRONG_PRINCIPAL The principal that received the authentication request is not the same as the one passed into the pszTargetName parameter. This indicates a failure in mutual authentication.";
  201. } else {
  202. worker->error = TRUE;
  203. worker->error_code = status;
  204. worker->error_message = DisplaySECError(status);
  205. }
  206. // Clean up data
  207. if(call->decoded_input_str != NULL) free(call->decoded_input_str);
  208. if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
  209. }
  210. static Local<Value> _map_initializeContext(Worker *worker) {
  211. // Unwrap the security context
  212. SecurityContext *context = (SecurityContext *)worker->return_value;
  213. // Return the value
  214. return context->handle();
  215. }
  216. NAN_METHOD(SecurityContext::InitializeContext) {
  217. char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
  218. int decoded_input_str_length = NULL;
  219. // Store reference to security credentials
  220. SecurityCredentials *security_credentials = NULL;
  221. // We need 3 parameters
  222. if(info.Length() != 4)
  223. return Nan::ThrowError("Initialize must be called with [credential:SecurityCredential, servicePrincipalName:string, input:string, callback:function]");
  224. // First parameter must be an instance of SecurityCredentials
  225. if(!SecurityCredentials::HasInstance(info[0]))
  226. return Nan::ThrowError("First parameter for Initialize must be an instance of SecurityCredentials");
  227. // Second parameter must be a string
  228. if(!info[1]->IsString())
  229. return Nan::ThrowError("Second parameter for Initialize must be a string");
  230. // Third parameter must be a base64 encoded string
  231. if(!info[2]->IsString())
  232. return Nan::ThrowError("Second parameter for Initialize must be a string");
  233. // Third parameter must be a callback
  234. if(!info[3]->IsFunction())
  235. return Nan::ThrowError("Third parameter for Initialize must be a callback function");
  236. // Let's unpack the values
  237. Local<String> service_principal_name = info[1]->ToString();
  238. service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
  239. service_principal_name->WriteUtf8(service_principal_name_str);
  240. // Unpack the user name
  241. Local<String> input = info[2]->ToString();
  242. if(input->Utf8Length() > 0) {
  243. input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
  244. input->WriteUtf8(input_str);
  245. // Now let's get the base64 decoded string
  246. decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
  247. // Free original allocation
  248. free(input_str);
  249. }
  250. // Unpack the Security credentials
  251. security_credentials = Nan::ObjectWrap::Unwrap<SecurityCredentials>(info[0]->ToObject());
  252. // Create Security context instance
  253. Local<Object> security_context_value = Nan::New(constructor_template)->GetFunction()->NewInstance();
  254. // Unwrap the security context
  255. SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(security_context_value);
  256. // Add a reference to the security_credentials
  257. security_context->security_credentials = security_credentials;
  258. // Build the call function
  259. SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)calloc(1, sizeof(SecurityContextStaticInitializeCall));
  260. call->context = security_context;
  261. call->decoded_input_str = decoded_input_str;
  262. call->decoded_input_str_length = decoded_input_str_length;
  263. call->service_principal_name_str = service_principal_name_str;
  264. // Callback
  265. Local<Function> callback = Local<Function>::Cast(info[3]);
  266. // Let's allocate some space
  267. Worker *worker = new Worker();
  268. worker->error = false;
  269. worker->request.data = worker;
  270. worker->callback = new Nan::Callback(callback);
  271. worker->parameters = call;
  272. worker->execute = _initializeContext;
  273. worker->mapper = _map_initializeContext;
  274. // Schedule the worker with lib_uv
  275. uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
  276. // Return no value as it's callback based
  277. info.GetReturnValue().Set(Nan::Undefined());
  278. }
  279. NAN_GETTER(SecurityContext::PayloadGetter) {
  280. // Unpack the context object
  281. SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  282. // Return the low bits
  283. info.GetReturnValue().Set(Nan::New<String>(context->payload).ToLocalChecked());
  284. }
  285. NAN_GETTER(SecurityContext::HasContextGetter) {
  286. // Unpack the context object
  287. SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  288. // Return the low bits
  289. info.GetReturnValue().Set(Nan::New<Boolean>(context->hasContext));
  290. }
  291. //
  292. // Async InitializeContextStep
  293. //
  294. typedef struct SecurityContextStepStaticInitializeCall {
  295. char *service_principal_name_str;
  296. char *decoded_input_str;
  297. int decoded_input_str_length;
  298. SecurityContext *context;
  299. } SecurityContextStepStaticInitializeCall;
  300. static void _initializeContextStep(Worker *worker) {
  301. // Outbound data array
  302. BYTE *out_bound_data_str = NULL;
  303. // Status of operation
  304. SECURITY_STATUS status;
  305. // Unpack data
  306. SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)worker->parameters;
  307. SecurityContext *context = call->context;
  308. // Structures used for c calls
  309. SecBufferDesc ibd, obd;
  310. SecBuffer ib, ob;
  311. //
  312. // Prepare data structure for returned data from SSPI
  313. ob.BufferType = SECBUFFER_TOKEN;
  314. ob.cbBuffer = context->m_PkgInfo->cbMaxToken;
  315. // Allocate space for return data
  316. out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
  317. ob.pvBuffer = out_bound_data_str;
  318. // prepare buffer description
  319. obd.cBuffers = 1;
  320. obd.ulVersion = SECBUFFER_VERSION;
  321. obd.pBuffers = &ob;
  322. //
  323. // Prepare the data we are passing to the SSPI method
  324. if(call->decoded_input_str_length > 0) {
  325. ib.BufferType = SECBUFFER_TOKEN;
  326. ib.cbBuffer = call->decoded_input_str_length;
  327. ib.pvBuffer = call->decoded_input_str;
  328. // prepare buffer description
  329. ibd.cBuffers = 1;
  330. ibd.ulVersion = SECBUFFER_VERSION;
  331. ibd.pBuffers = &ib;
  332. }
  333. // Perform initialization step
  334. status = _sspi_initializeSecurityContext(
  335. &context->security_credentials->m_Credentials
  336. , context->hasContext == true ? &context->m_Context : NULL
  337. , const_cast<TCHAR*>(call->service_principal_name_str)
  338. , 0x02 // MUTUAL
  339. , 0
  340. , 0 // Network
  341. , call->decoded_input_str_length ? &ibd : NULL
  342. , 0
  343. , &context->m_Context
  344. , &obd
  345. , &context->CtxtAttr
  346. , &context->Expiration
  347. );
  348. // If we have a ok or continue let's prepare the result
  349. if(status == SEC_E_OK
  350. || status == SEC_I_COMPLETE_NEEDED
  351. || status == SEC_I_CONTINUE_NEEDED
  352. || status == SEC_I_COMPLETE_AND_CONTINUE
  353. ) {
  354. // Set the new payload
  355. if(context->payload != NULL) free(context->payload);
  356. context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
  357. worker->return_code = status;
  358. worker->return_value = context;
  359. } else {
  360. worker->error = TRUE;
  361. worker->error_code = status;
  362. worker->error_message = DisplaySECError(status);
  363. }
  364. // Clean up data
  365. if(call->decoded_input_str != NULL) free(call->decoded_input_str);
  366. if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
  367. }
  368. static Local<Value> _map_initializeContextStep(Worker *worker) {
  369. // Unwrap the security context
  370. SecurityContext *context = (SecurityContext *)worker->return_value;
  371. // Return the value
  372. return context->handle();
  373. }
  374. NAN_METHOD(SecurityContext::InitalizeStep) {
  375. char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
  376. int decoded_input_str_length = NULL;
  377. // We need 3 parameters
  378. if(info.Length() != 3)
  379. return Nan::ThrowError("Initialize must be called with [servicePrincipalName:string, input:string, callback:function]");
  380. // Second parameter must be a string
  381. if(!info[0]->IsString())
  382. return Nan::ThrowError("First parameter for Initialize must be a string");
  383. // Third parameter must be a base64 encoded string
  384. if(!info[1]->IsString())
  385. return Nan::ThrowError("Second parameter for Initialize must be a string");
  386. // Third parameter must be a base64 encoded string
  387. if(!info[2]->IsFunction())
  388. return Nan::ThrowError("Third parameter for Initialize must be a callback function");
  389. // Let's unpack the values
  390. Local<String> service_principal_name = info[0]->ToString();
  391. service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
  392. service_principal_name->WriteUtf8(service_principal_name_str);
  393. // Unpack the user name
  394. Local<String> input = info[1]->ToString();
  395. if(input->Utf8Length() > 0) {
  396. input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
  397. input->WriteUtf8(input_str);
  398. // Now let's get the base64 decoded string
  399. decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
  400. // Free input string
  401. free(input_str);
  402. }
  403. // Unwrap the security context
  404. SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  405. // Create call structure
  406. SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)calloc(1, sizeof(SecurityContextStepStaticInitializeCall));
  407. call->context = security_context;
  408. call->decoded_input_str = decoded_input_str;
  409. call->decoded_input_str_length = decoded_input_str_length;
  410. call->service_principal_name_str = service_principal_name_str;
  411. // Callback
  412. Local<Function> callback = Local<Function>::Cast(info[2]);
  413. // Let's allocate some space
  414. Worker *worker = new Worker();
  415. worker->error = false;
  416. worker->request.data = worker;
  417. worker->callback = new Nan::Callback(callback);
  418. worker->parameters = call;
  419. worker->execute = _initializeContextStep;
  420. worker->mapper = _map_initializeContextStep;
  421. // Schedule the worker with lib_uv
  422. uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
  423. // Return no value as it's callback based
  424. info.GetReturnValue().Set(Nan::Undefined());
  425. }
  426. //
  427. // Async EncryptMessage
  428. //
  429. typedef struct SecurityContextEncryptMessageCall {
  430. SecurityContext *context;
  431. SecurityBufferDescriptor *descriptor;
  432. unsigned long flags;
  433. } SecurityContextEncryptMessageCall;
  434. static void _encryptMessage(Worker *worker) {
  435. SECURITY_STATUS status;
  436. // Unpack call
  437. SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)worker->parameters;
  438. // Unpack the security context
  439. SecurityContext *context = call->context;
  440. SecurityBufferDescriptor *descriptor = call->descriptor;
  441. // Let's execute encryption
  442. status = _sspi_EncryptMessage(
  443. &context->m_Context
  444. , call->flags
  445. , &descriptor->secBufferDesc
  446. , 0
  447. );
  448. // We've got ok
  449. if(status == SEC_E_OK) {
  450. int bytesToAllocate = (int)descriptor->bufferSize();
  451. // Free up existing payload
  452. if(context->payload != NULL) free(context->payload);
  453. // Save the payload
  454. context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
  455. // Set result
  456. worker->return_code = status;
  457. worker->return_value = context;
  458. } else {
  459. worker->error = TRUE;
  460. worker->error_code = status;
  461. worker->error_message = DisplaySECError(status);
  462. }
  463. }
  464. static Local<Value> _map_encryptMessage(Worker *worker) {
  465. // Unwrap the security context
  466. SecurityContext *context = (SecurityContext *)worker->return_value;
  467. // Return the value
  468. return context->handle();
  469. }
  470. NAN_METHOD(SecurityContext::EncryptMessage) {
  471. if(info.Length() != 3)
  472. return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
  473. if(!SecurityBufferDescriptor::HasInstance(info[0]))
  474. return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
  475. if(!info[1]->IsUint32())
  476. return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
  477. if(!info[2]->IsFunction())
  478. return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
  479. // Unpack the security context
  480. SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  481. // Unpack the descriptor
  482. SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
  483. // Create call structure
  484. SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)calloc(1, sizeof(SecurityContextEncryptMessageCall));
  485. call->context = security_context;
  486. call->descriptor = descriptor;
  487. call->flags = (unsigned long)info[1]->ToInteger()->Value();
  488. // Callback
  489. Local<Function> callback = Local<Function>::Cast(info[2]);
  490. // Let's allocate some space
  491. Worker *worker = new Worker();
  492. worker->error = false;
  493. worker->request.data = worker;
  494. worker->callback = new Nan::Callback(callback);
  495. worker->parameters = call;
  496. worker->execute = _encryptMessage;
  497. worker->mapper = _map_encryptMessage;
  498. // Schedule the worker with lib_uv
  499. uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
  500. // Return no value as it's callback based
  501. info.GetReturnValue().Set(Nan::Undefined());
  502. }
  503. //
  504. // Async DecryptMessage
  505. //
  506. typedef struct SecurityContextDecryptMessageCall {
  507. SecurityContext *context;
  508. SecurityBufferDescriptor *descriptor;
  509. } SecurityContextDecryptMessageCall;
  510. static void _decryptMessage(Worker *worker) {
  511. unsigned long quality = 0;
  512. SECURITY_STATUS status;
  513. // Unpack parameters
  514. SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)worker->parameters;
  515. SecurityContext *context = call->context;
  516. SecurityBufferDescriptor *descriptor = call->descriptor;
  517. // Let's execute encryption
  518. status = _sspi_DecryptMessage(
  519. &context->m_Context
  520. , &descriptor->secBufferDesc
  521. , 0
  522. , (unsigned long)&quality
  523. );
  524. // We've got ok
  525. if(status == SEC_E_OK) {
  526. int bytesToAllocate = (int)descriptor->bufferSize();
  527. // Free up existing payload
  528. if(context->payload != NULL) free(context->payload);
  529. // Save the payload
  530. context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
  531. // Set return values
  532. worker->return_code = status;
  533. worker->return_value = context;
  534. } else {
  535. worker->error = TRUE;
  536. worker->error_code = status;
  537. worker->error_message = DisplaySECError(status);
  538. }
  539. }
  540. static Local<Value> _map_decryptMessage(Worker *worker) {
  541. // Unwrap the security context
  542. SecurityContext *context = (SecurityContext *)worker->return_value;
  543. // Return the value
  544. return context->handle();
  545. }
  546. NAN_METHOD(SecurityContext::DecryptMessage) {
  547. if(info.Length() != 2)
  548. return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
  549. if(!SecurityBufferDescriptor::HasInstance(info[0]))
  550. return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
  551. if(!info[1]->IsFunction())
  552. return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
  553. // Unpack the security context
  554. SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  555. // Unpack the descriptor
  556. SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
  557. // Create call structure
  558. SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)calloc(1, sizeof(SecurityContextDecryptMessageCall));
  559. call->context = security_context;
  560. call->descriptor = descriptor;
  561. // Callback
  562. Local<Function> callback = Local<Function>::Cast(info[1]);
  563. // Let's allocate some space
  564. Worker *worker = new Worker();
  565. worker->error = false;
  566. worker->request.data = worker;
  567. worker->callback = new Nan::Callback(callback);
  568. worker->parameters = call;
  569. worker->execute = _decryptMessage;
  570. worker->mapper = _map_decryptMessage;
  571. // Schedule the worker with lib_uv
  572. uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
  573. // Return no value as it's callback based
  574. info.GetReturnValue().Set(Nan::Undefined());
  575. }
  576. //
  577. // Async QueryContextAttributes
  578. //
  579. typedef struct SecurityContextQueryContextAttributesCall {
  580. SecurityContext *context;
  581. uint32_t attribute;
  582. } SecurityContextQueryContextAttributesCall;
  583. static void _queryContextAttributes(Worker *worker) {
  584. SECURITY_STATUS status;
  585. // Cast to data structure
  586. SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
  587. // Allocate some space
  588. SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)calloc(1, sizeof(SecPkgContext_Sizes));
  589. // Let's grab the query context attribute
  590. status = _sspi_QueryContextAttributes(
  591. &call->context->m_Context,
  592. call->attribute,
  593. sizes
  594. );
  595. if(status == SEC_E_OK) {
  596. worker->return_code = status;
  597. worker->return_value = sizes;
  598. } else {
  599. worker->error = TRUE;
  600. worker->error_code = status;
  601. worker->error_message = DisplaySECError(status);
  602. }
  603. }
  604. static Local<Value> _map_queryContextAttributes(Worker *worker) {
  605. // Cast to data structure
  606. SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
  607. // Unpack the attribute
  608. uint32_t attribute = call->attribute;
  609. // Convert data
  610. if(attribute == SECPKG_ATTR_SIZES) {
  611. SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)worker->return_value;
  612. // Create object
  613. Local<Object> value = Nan::New<Object>();
  614. value->Set(Nan::New<String>("maxToken").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxToken)));
  615. value->Set(Nan::New<String>("maxSignature").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxSignature)));
  616. value->Set(Nan::New<String>("blockSize").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbBlockSize)));
  617. value->Set(Nan::New<String>("securityTrailer").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbSecurityTrailer)));
  618. return value;
  619. }
  620. // Return the value
  621. return Nan::Null();
  622. }
  623. NAN_METHOD(SecurityContext::QueryContextAttributes) {
  624. if(info.Length() != 2)
  625. return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
  626. if(!info[0]->IsInt32())
  627. return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
  628. if(!info[1]->IsFunction())
  629. return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
  630. // Unpack the security context
  631. SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
  632. // Unpack the int value
  633. uint32_t attribute = info[0]->ToInt32()->Value();
  634. // Check that we have a supported attribute
  635. if(attribute != SECPKG_ATTR_SIZES)
  636. return Nan::ThrowError("QueryContextAttributes only supports the SECPKG_ATTR_SIZES attribute");
  637. // Create call structure
  638. SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)calloc(1, sizeof(SecurityContextQueryContextAttributesCall));
  639. call->attribute = attribute;
  640. call->context = security_context;
  641. // Callback
  642. Local<Function> callback = Local<Function>::Cast(info[1]);
  643. // Let's allocate some space
  644. Worker *worker = new Worker();
  645. worker->error = false;
  646. worker->request.data = worker;
  647. worker->callback = new Nan::Callback(callback);
  648. worker->parameters = call;
  649. worker->execute = _queryContextAttributes;
  650. worker->mapper = _map_queryContextAttributes;
  651. // Schedule the worker with lib_uv
  652. uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
  653. // Return no value as it's callback based
  654. info.GetReturnValue().Set(Nan::Undefined());
  655. }
  656. void SecurityContext::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
  657. // Grab the scope of the call from Node
  658. Nan::HandleScope scope;
  659. // Define a new function template
  660. Local<FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(SecurityContext::New));
  661. t->InstanceTemplate()->SetInternalFieldCount(1);
  662. t->SetClassName(Nan::New<String>("SecurityContext").ToLocalChecked());
  663. // Class methods
  664. Nan::SetMethod(t, "initialize", SecurityContext::InitializeContext);
  665. // Set up method for the instance
  666. Nan::SetPrototypeMethod(t, "initialize", SecurityContext::InitalizeStep);
  667. Nan::SetPrototypeMethod(t, "decryptMessage", SecurityContext::DecryptMessage);
  668. Nan::SetPrototypeMethod(t, "queryContextAttributes", SecurityContext::QueryContextAttributes);
  669. Nan::SetPrototypeMethod(t, "encryptMessage", SecurityContext::EncryptMessage);
  670. // Get prototype
  671. Local<ObjectTemplate> proto = t->PrototypeTemplate();
  672. // Getter for the response
  673. Nan::SetAccessor(proto, Nan::New<String>("payload").ToLocalChecked(), SecurityContext::PayloadGetter);
  674. Nan::SetAccessor(proto, Nan::New<String>("hasContext").ToLocalChecked(), SecurityContext::HasContextGetter);
  675. // Set persistent
  676. SecurityContext::constructor_template.Reset(t);
  677. // Set the symbol
  678. target->ForceSet(Nan::New<String>("SecurityContext").ToLocalChecked(), t->GetFunction());
  679. }
  680. static LPSTR DisplaySECError(DWORD ErrCode) {
  681. LPSTR pszName = NULL; // WinError.h
  682. switch(ErrCode) {
  683. case SEC_E_BUFFER_TOO_SMALL:
  684. pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
  685. break;
  686. case SEC_E_CRYPTO_SYSTEM_INVALID:
  687. pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
  688. break;
  689. case SEC_E_INCOMPLETE_MESSAGE:
  690. pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessageSync (General) again.";
  691. break;
  692. case SEC_E_INVALID_HANDLE:
  693. pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
  694. break;
  695. case SEC_E_INVALID_TOKEN:
  696. pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
  697. break;
  698. case SEC_E_MESSAGE_ALTERED:
  699. pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
  700. break;
  701. case SEC_E_OUT_OF_SEQUENCE:
  702. pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
  703. break;
  704. case SEC_E_QOP_NOT_SUPPORTED:
  705. pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
  706. break;
  707. case SEC_I_CONTEXT_EXPIRED:
  708. pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
  709. break;
  710. case SEC_I_RENEGOTIATE:
  711. pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
  712. break;
  713. case SEC_E_ENCRYPT_FAILURE:
  714. pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
  715. break;
  716. case SEC_E_DECRYPT_FAILURE:
  717. pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
  718. break;
  719. case -1:
  720. pszName = "Failed to load security.dll library";
  721. break;
  722. }
  723. return pszName;
  724. }