nsAuthSambaNTLM.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* vim:set ts=4 sw=4 et cindent: */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsAuth.h"
  6. #include "nsAuthSambaNTLM.h"
  7. #include "nspr.h"
  8. #include "prenv.h"
  9. #include "plbase64.h"
  10. #include "prerror.h"
  11. #include <stdlib.h>
  12. nsAuthSambaNTLM::nsAuthSambaNTLM()
  13. : mInitialMessage(nullptr), mChildPID(nullptr), mFromChildFD(nullptr),
  14. mToChildFD(nullptr)
  15. {
  16. }
  17. nsAuthSambaNTLM::~nsAuthSambaNTLM()
  18. {
  19. // ntlm_auth reads from stdin regularly so closing our file handles
  20. // should cause it to exit.
  21. Shutdown();
  22. PR_Free(mInitialMessage);
  23. }
  24. void
  25. nsAuthSambaNTLM::Shutdown()
  26. {
  27. if (mFromChildFD) {
  28. PR_Close(mFromChildFD);
  29. mFromChildFD = nullptr;
  30. }
  31. if (mToChildFD) {
  32. PR_Close(mToChildFD);
  33. mToChildFD = nullptr;
  34. }
  35. if (mChildPID) {
  36. int32_t exitCode;
  37. PR_WaitProcess(mChildPID, &exitCode);
  38. mChildPID = nullptr;
  39. }
  40. }
  41. NS_IMPL_ISUPPORTS(nsAuthSambaNTLM, nsIAuthModule)
  42. static bool
  43. SpawnIOChild(char* const* aArgs, PRProcess** aPID,
  44. PRFileDesc** aFromChildFD, PRFileDesc** aToChildFD)
  45. {
  46. PRFileDesc* toChildPipeRead;
  47. PRFileDesc* toChildPipeWrite;
  48. if (PR_CreatePipe(&toChildPipeRead, &toChildPipeWrite) != PR_SUCCESS)
  49. return false;
  50. PR_SetFDInheritable(toChildPipeRead, true);
  51. PR_SetFDInheritable(toChildPipeWrite, false);
  52. PRFileDesc* fromChildPipeRead;
  53. PRFileDesc* fromChildPipeWrite;
  54. if (PR_CreatePipe(&fromChildPipeRead, &fromChildPipeWrite) != PR_SUCCESS) {
  55. PR_Close(toChildPipeRead);
  56. PR_Close(toChildPipeWrite);
  57. return false;
  58. }
  59. PR_SetFDInheritable(fromChildPipeRead, false);
  60. PR_SetFDInheritable(fromChildPipeWrite, true);
  61. PRProcessAttr* attr = PR_NewProcessAttr();
  62. if (!attr) {
  63. PR_Close(fromChildPipeRead);
  64. PR_Close(fromChildPipeWrite);
  65. PR_Close(toChildPipeRead);
  66. PR_Close(toChildPipeWrite);
  67. return false;
  68. }
  69. PR_ProcessAttrSetStdioRedirect(attr, PR_StandardInput, toChildPipeRead);
  70. PR_ProcessAttrSetStdioRedirect(attr, PR_StandardOutput, fromChildPipeWrite);
  71. PRProcess* process = PR_CreateProcess(aArgs[0], aArgs, nullptr, attr);
  72. PR_DestroyProcessAttr(attr);
  73. PR_Close(fromChildPipeWrite);
  74. PR_Close(toChildPipeRead);
  75. if (!process) {
  76. LOG(("ntlm_auth exec failure [%d]", PR_GetError()));
  77. PR_Close(fromChildPipeRead);
  78. PR_Close(toChildPipeWrite);
  79. return false;
  80. }
  81. *aPID = process;
  82. *aFromChildFD = fromChildPipeRead;
  83. *aToChildFD = toChildPipeWrite;
  84. return true;
  85. }
  86. static bool WriteString(PRFileDesc* aFD, const nsACString& aString)
  87. {
  88. int32_t length = aString.Length();
  89. const char* s = aString.BeginReading();
  90. LOG(("Writing to ntlm_auth: %s", s));
  91. while (length > 0) {
  92. int result = PR_Write(aFD, s, length);
  93. if (result <= 0)
  94. return false;
  95. s += result;
  96. length -= result;
  97. }
  98. return true;
  99. }
  100. static bool ReadLine(PRFileDesc* aFD, nsACString& aString)
  101. {
  102. // ntlm_auth is defined to only send one line in response to each of our
  103. // input lines. So this simple unbuffered strategy works as long as we
  104. // read the response immediately after sending one request.
  105. aString.Truncate();
  106. for (;;) {
  107. char buf[1024];
  108. int result = PR_Read(aFD, buf, sizeof(buf));
  109. if (result <= 0)
  110. return false;
  111. aString.Append(buf, result);
  112. if (buf[result - 1] == '\n') {
  113. LOG(("Read from ntlm_auth: %s", nsPromiseFlatCString(aString).get()));
  114. return true;
  115. }
  116. }
  117. }
  118. /**
  119. * Returns a heap-allocated array of PRUint8s, and stores the length in aLen.
  120. * Returns nullptr if there's an error of any kind.
  121. */
  122. static uint8_t* ExtractMessage(const nsACString& aLine, uint32_t* aLen)
  123. {
  124. // ntlm_auth sends blobs to us as base64-encoded strings after the "xx "
  125. // preamble on the response line.
  126. int32_t length = aLine.Length();
  127. // The caller should verify there is a valid "xx " prefix and the line
  128. // is terminated with a \n
  129. NS_ASSERTION(length >= 4, "Line too short...");
  130. const char* line = aLine.BeginReading();
  131. const char* s = line + 3;
  132. length -= 4; // lose first 3 chars plus trailing \n
  133. NS_ASSERTION(s[length] == '\n', "aLine not newline-terminated");
  134. if (length & 3) {
  135. // The base64 encoded block must be multiple of 4. If not, something
  136. // screwed up.
  137. NS_WARNING("Base64 encoded block should be a multiple of 4 chars");
  138. return nullptr;
  139. }
  140. // Calculate the exact length. I wonder why there isn't a function for this
  141. // in plbase64.
  142. int32_t numEquals;
  143. for (numEquals = 0; numEquals < length; ++numEquals) {
  144. if (s[length - 1 - numEquals] != '=')
  145. break;
  146. }
  147. *aLen = (length/4)*3 - numEquals;
  148. return reinterpret_cast<uint8_t*>(PL_Base64Decode(s, length, nullptr));
  149. }
  150. nsresult
  151. nsAuthSambaNTLM::SpawnNTLMAuthHelper()
  152. {
  153. const char* username = PR_GetEnv("USER");
  154. if (!username)
  155. return NS_ERROR_FAILURE;
  156. const char* const args[] = {
  157. "ntlm_auth",
  158. "--helper-protocol", "ntlmssp-client-1",
  159. "--use-cached-creds",
  160. "--username", username,
  161. nullptr
  162. };
  163. bool isOK = SpawnIOChild(const_cast<char* const*>(args), &mChildPID, &mFromChildFD, &mToChildFD);
  164. if (!isOK)
  165. return NS_ERROR_FAILURE;
  166. if (!WriteString(mToChildFD, NS_LITERAL_CSTRING("YR\n")))
  167. return NS_ERROR_FAILURE;
  168. nsCString line;
  169. if (!ReadLine(mFromChildFD, line))
  170. return NS_ERROR_FAILURE;
  171. if (!StringBeginsWith(line, NS_LITERAL_CSTRING("YR "))) {
  172. // Something went wrong. Perhaps no credentials are accessible.
  173. return NS_ERROR_FAILURE;
  174. }
  175. // It gave us an initial client-to-server request packet. Save that
  176. // because we'll need it later.
  177. mInitialMessage = ExtractMessage(line, &mInitialMessageLen);
  178. if (!mInitialMessage)
  179. return NS_ERROR_FAILURE;
  180. return NS_OK;
  181. }
  182. NS_IMETHODIMP
  183. nsAuthSambaNTLM::Init(const char *serviceName,
  184. uint32_t serviceFlags,
  185. const char16_t *domain,
  186. const char16_t *username,
  187. const char16_t *password)
  188. {
  189. NS_ASSERTION(!username && !domain && !password, "unexpected credentials");
  190. return NS_OK;
  191. }
  192. NS_IMETHODIMP
  193. nsAuthSambaNTLM::GetNextToken(const void *inToken,
  194. uint32_t inTokenLen,
  195. void **outToken,
  196. uint32_t *outTokenLen)
  197. {
  198. if (!inToken) {
  199. /* someone wants our initial message */
  200. *outToken = nsMemory::Clone(mInitialMessage, mInitialMessageLen);
  201. if (!*outToken)
  202. return NS_ERROR_OUT_OF_MEMORY;
  203. *outTokenLen = mInitialMessageLen;
  204. return NS_OK;
  205. }
  206. /* inToken must be a type 2 message. Get ntlm_auth to generate our response */
  207. char* encoded = PL_Base64Encode(static_cast<const char*>(inToken), inTokenLen, nullptr);
  208. if (!encoded)
  209. return NS_ERROR_OUT_OF_MEMORY;
  210. nsCString request;
  211. request.AssignLiteral("TT ");
  212. request.Append(encoded);
  213. PR_Free(encoded);
  214. request.Append('\n');
  215. if (!WriteString(mToChildFD, request))
  216. return NS_ERROR_FAILURE;
  217. nsCString line;
  218. if (!ReadLine(mFromChildFD, line))
  219. return NS_ERROR_FAILURE;
  220. if (!StringBeginsWith(line, NS_LITERAL_CSTRING("KK ")) &&
  221. !StringBeginsWith(line, NS_LITERAL_CSTRING("AF "))) {
  222. // Something went wrong. Perhaps no credentials are accessible.
  223. return NS_ERROR_FAILURE;
  224. }
  225. uint8_t* buf = ExtractMessage(line, outTokenLen);
  226. if (!buf)
  227. return NS_ERROR_FAILURE;
  228. *outToken = nsMemory::Clone(buf, *outTokenLen);
  229. PR_Free(buf);
  230. if (!*outToken) {
  231. return NS_ERROR_OUT_OF_MEMORY;
  232. }
  233. // We're done. Close our file descriptors now and reap the helper
  234. // process.
  235. Shutdown();
  236. return NS_SUCCESS_AUTH_FINISHED;
  237. }
  238. NS_IMETHODIMP
  239. nsAuthSambaNTLM::Unwrap(const void *inToken,
  240. uint32_t inTokenLen,
  241. void **outToken,
  242. uint32_t *outTokenLen)
  243. {
  244. return NS_ERROR_NOT_IMPLEMENTED;
  245. }
  246. NS_IMETHODIMP
  247. nsAuthSambaNTLM::Wrap(const void *inToken,
  248. uint32_t inTokenLen,
  249. bool confidential,
  250. void **outToken,
  251. uint32_t *outTokenLen)
  252. {
  253. return NS_ERROR_NOT_IMPLEMENTED;
  254. }