curl_ntlm_wb.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /***************************************************************************
  2. * _ _ ____ _
  3. * Project ___| | | | _ \| |
  4. * / __| | | | |_) | |
  5. * | (__| |_| | _ <| |___
  6. * \___|\___/|_| \_\_____|
  7. *
  8. * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
  9. *
  10. * This software is licensed as described in the file COPYING, which
  11. * you should have received as part of this distribution. The terms
  12. * are also available at http://curl.haxx.se/docs/copyright.html.
  13. *
  14. * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  15. * copies of the Software, and permit persons to whom the Software is
  16. * furnished to do so, under the terms of the COPYING file.
  17. *
  18. * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  19. * KIND, either express or implied.
  20. *
  21. ***************************************************************************/
  22. #include "curl_setup.h"
  23. #if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
  24. /*
  25. * NTLM details:
  26. *
  27. * http://davenport.sourceforge.net/ntlm.html
  28. * http://www.innovation.ch/java/ntlm.html
  29. */
  30. #define DEBUG_ME 0
  31. #ifdef HAVE_SYS_WAIT_H
  32. #include <sys/wait.h>
  33. #endif
  34. #ifdef HAVE_SIGNAL_H
  35. #include <signal.h>
  36. #endif
  37. #include "urldata.h"
  38. #include "sendf.h"
  39. #include "select.h"
  40. #include "curl_ntlm_wb.h"
  41. #include "url.h"
  42. #include "strerror.h"
  43. #include "curl_memory.h"
  44. #define _MPRINTF_REPLACE /* use our functions only */
  45. #include <curl/mprintf.h>
  46. /* The last #include file should be: */
  47. #include "memdebug.h"
  48. #if DEBUG_ME
  49. # define DEBUG_OUT(x) x
  50. #else
  51. # define DEBUG_OUT(x) Curl_nop_stmt
  52. #endif
  53. /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
  54. to avoid fooling the socket leak detector */
  55. #if defined(HAVE_CLOSESOCKET)
  56. # define sclose_nolog(x) closesocket((x))
  57. #elif defined(HAVE_CLOSESOCKET_CAMEL)
  58. # define sclose_nolog(x) CloseSocket((x))
  59. #else
  60. # define sclose_nolog(x) close((x))
  61. #endif
  62. void Curl_ntlm_wb_cleanup(struct connectdata *conn)
  63. {
  64. if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
  65. sclose(conn->ntlm_auth_hlpr_socket);
  66. conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
  67. }
  68. if(conn->ntlm_auth_hlpr_pid) {
  69. int i;
  70. for(i = 0; i < 4; i++) {
  71. pid_t ret = waitpid(conn->ntlm_auth_hlpr_pid, NULL, WNOHANG);
  72. if(ret == conn->ntlm_auth_hlpr_pid || errno == ECHILD)
  73. break;
  74. switch(i) {
  75. case 0:
  76. kill(conn->ntlm_auth_hlpr_pid, SIGTERM);
  77. break;
  78. case 1:
  79. /* Give the process another moment to shut down cleanly before
  80. bringing down the axe */
  81. Curl_wait_ms(1);
  82. break;
  83. case 2:
  84. kill(conn->ntlm_auth_hlpr_pid, SIGKILL);
  85. break;
  86. case 3:
  87. break;
  88. }
  89. }
  90. conn->ntlm_auth_hlpr_pid = 0;
  91. }
  92. Curl_safefree(conn->challenge_header);
  93. conn->challenge_header = NULL;
  94. Curl_safefree(conn->response_header);
  95. conn->response_header = NULL;
  96. }
  97. static CURLcode ntlm_wb_init(struct connectdata *conn, const char *userp)
  98. {
  99. curl_socket_t sockfds[2];
  100. pid_t child_pid;
  101. const char *username;
  102. char *slash, *domain = NULL;
  103. const char *ntlm_auth = NULL;
  104. char *ntlm_auth_alloc = NULL;
  105. int error;
  106. /* Return if communication with ntlm_auth already set up */
  107. if(conn->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
  108. conn->ntlm_auth_hlpr_pid)
  109. return CURLE_OK;
  110. username = userp;
  111. slash = strpbrk(username, "\\/");
  112. if(slash) {
  113. if((domain = strdup(username)) == NULL)
  114. return CURLE_OUT_OF_MEMORY;
  115. slash = domain + (slash - username);
  116. *slash = '\0';
  117. username = username + (slash - domain) + 1;
  118. }
  119. /* For testing purposes, when DEBUGBUILD is defined and environment
  120. variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
  121. NTLM challenge/response which only accepts commands and output
  122. strings pre-written in test case definitions */
  123. #ifdef DEBUGBUILD
  124. ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
  125. if(ntlm_auth_alloc)
  126. ntlm_auth = ntlm_auth_alloc;
  127. else
  128. #endif
  129. ntlm_auth = NTLM_WB_FILE;
  130. if(access(ntlm_auth, X_OK) != 0) {
  131. error = ERRNO;
  132. failf(conn->data, "Could not access ntlm_auth: %s errno %d: %s",
  133. ntlm_auth, error, Curl_strerror(conn, error));
  134. goto done;
  135. }
  136. if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
  137. error = ERRNO;
  138. failf(conn->data, "Could not open socket pair. errno %d: %s",
  139. error, Curl_strerror(conn, error));
  140. goto done;
  141. }
  142. child_pid = fork();
  143. if(child_pid == -1) {
  144. error = ERRNO;
  145. sclose(sockfds[0]);
  146. sclose(sockfds[1]);
  147. failf(conn->data, "Could not fork. errno %d: %s",
  148. error, Curl_strerror(conn, error));
  149. goto done;
  150. }
  151. else if(!child_pid) {
  152. /*
  153. * child process
  154. */
  155. /* Don't use sclose in the child since it fools the socket leak detector */
  156. sclose_nolog(sockfds[0]);
  157. if(dup2(sockfds[1], STDIN_FILENO) == -1) {
  158. error = ERRNO;
  159. failf(conn->data, "Could not redirect child stdin. errno %d: %s",
  160. error, Curl_strerror(conn, error));
  161. exit(1);
  162. }
  163. if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
  164. error = ERRNO;
  165. failf(conn->data, "Could not redirect child stdout. errno %d: %s",
  166. error, Curl_strerror(conn, error));
  167. exit(1);
  168. }
  169. if(domain)
  170. execl(ntlm_auth, ntlm_auth,
  171. "--helper-protocol", "ntlmssp-client-1",
  172. "--use-cached-creds",
  173. "--username", username,
  174. "--domain", domain,
  175. NULL);
  176. else
  177. execl(ntlm_auth, ntlm_auth,
  178. "--helper-protocol", "ntlmssp-client-1",
  179. "--use-cached-creds",
  180. "--username", username,
  181. NULL);
  182. error = ERRNO;
  183. sclose_nolog(sockfds[1]);
  184. failf(conn->data, "Could not execl(). errno %d: %s",
  185. error, Curl_strerror(conn, error));
  186. exit(1);
  187. }
  188. sclose(sockfds[1]);
  189. conn->ntlm_auth_hlpr_socket = sockfds[0];
  190. conn->ntlm_auth_hlpr_pid = child_pid;
  191. Curl_safefree(domain);
  192. Curl_safefree(ntlm_auth_alloc);
  193. return CURLE_OK;
  194. done:
  195. Curl_safefree(domain);
  196. Curl_safefree(ntlm_auth_alloc);
  197. return CURLE_REMOTE_ACCESS_DENIED;
  198. }
  199. static CURLcode ntlm_wb_response(struct connectdata *conn,
  200. const char *input, curlntlm state)
  201. {
  202. ssize_t size;
  203. char buf[200]; /* enough, type 1, 3 message length is less then 200 */
  204. char *tmpbuf = buf;
  205. size_t len_in = strlen(input), len_out = sizeof(buf);
  206. while(len_in > 0) {
  207. ssize_t written = swrite(conn->ntlm_auth_hlpr_socket, input, len_in);
  208. if(written == -1) {
  209. /* Interrupted by a signal, retry it */
  210. if(errno == EINTR)
  211. continue;
  212. /* write failed if other errors happen */
  213. goto done;
  214. }
  215. input += written;
  216. len_in -= written;
  217. }
  218. /* Read one line */
  219. while(len_out > 0) {
  220. size = sread(conn->ntlm_auth_hlpr_socket, tmpbuf, len_out);
  221. if(size == -1) {
  222. if(errno == EINTR)
  223. continue;
  224. goto done;
  225. }
  226. else if(size == 0)
  227. goto done;
  228. else if(tmpbuf[size - 1] == '\n') {
  229. tmpbuf[size - 1] = '\0';
  230. goto wrfinish;
  231. }
  232. tmpbuf += size;
  233. len_out -= size;
  234. }
  235. goto done;
  236. wrfinish:
  237. /* Samba/winbind installed but not configured */
  238. if(state == NTLMSTATE_TYPE1 &&
  239. size == 3 &&
  240. buf[0] == 'P' && buf[1] == 'W')
  241. return CURLE_REMOTE_ACCESS_DENIED;
  242. /* invalid response */
  243. if(size < 4)
  244. goto done;
  245. if(state == NTLMSTATE_TYPE1 &&
  246. (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' '))
  247. goto done;
  248. if(state == NTLMSTATE_TYPE2 &&
  249. (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') &&
  250. (buf[0]!='A' || buf[1]!='F' || buf[2]!=' '))
  251. goto done;
  252. conn->response_header = aprintf("NTLM %.*s", size - 4, buf + 3);
  253. return CURLE_OK;
  254. done:
  255. return CURLE_REMOTE_ACCESS_DENIED;
  256. }
  257. /*
  258. * This is for creating ntlm header output by delegating challenge/response
  259. * to Samba's winbind daemon helper ntlm_auth.
  260. */
  261. CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
  262. bool proxy)
  263. {
  264. /* point to the address of the pointer that holds the string to send to the
  265. server, which is for a plain host or for a HTTP proxy */
  266. char **allocuserpwd;
  267. /* point to the name and password for this */
  268. const char *userp;
  269. /* point to the correct struct with this */
  270. struct ntlmdata *ntlm;
  271. struct auth *authp;
  272. CURLcode res = CURLE_OK;
  273. char *input;
  274. DEBUGASSERT(conn);
  275. DEBUGASSERT(conn->data);
  276. if(proxy) {
  277. allocuserpwd = &conn->allocptr.proxyuserpwd;
  278. userp = conn->proxyuser;
  279. ntlm = &conn->proxyntlm;
  280. authp = &conn->data->state.authproxy;
  281. }
  282. else {
  283. allocuserpwd = &conn->allocptr.userpwd;
  284. userp = conn->user;
  285. ntlm = &conn->ntlm;
  286. authp = &conn->data->state.authhost;
  287. }
  288. authp->done = FALSE;
  289. /* not set means empty */
  290. if(!userp)
  291. userp="";
  292. switch(ntlm->state) {
  293. case NTLMSTATE_TYPE1:
  294. default:
  295. /* Use Samba's 'winbind' daemon to support NTLM authentication,
  296. * by delegating the NTLM challenge/response protocal to a helper
  297. * in ntlm_auth.
  298. * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
  299. * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
  300. * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
  301. * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
  302. * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
  303. * filename of ntlm_auth helper.
  304. * If NTLM authentication using winbind fails, go back to original
  305. * request handling process.
  306. */
  307. /* Create communication with ntlm_auth */
  308. res = ntlm_wb_init(conn, userp);
  309. if(res)
  310. return res;
  311. res = ntlm_wb_response(conn, "YR\n", ntlm->state);
  312. if(res)
  313. return res;
  314. Curl_safefree(*allocuserpwd);
  315. *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
  316. proxy ? "Proxy-" : "",
  317. conn->response_header);
  318. DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
  319. Curl_safefree(conn->response_header);
  320. conn->response_header = NULL;
  321. break;
  322. case NTLMSTATE_TYPE2:
  323. input = aprintf("TT %s\n", conn->challenge_header);
  324. if(!input)
  325. return CURLE_OUT_OF_MEMORY;
  326. res = ntlm_wb_response(conn, input, ntlm->state);
  327. free(input);
  328. input = NULL;
  329. if(res)
  330. return res;
  331. Curl_safefree(*allocuserpwd);
  332. *allocuserpwd = aprintf("%sAuthorization: %s\r\n",
  333. proxy ? "Proxy-" : "",
  334. conn->response_header);
  335. DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
  336. ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
  337. authp->done = TRUE;
  338. Curl_ntlm_wb_cleanup(conn);
  339. break;
  340. case NTLMSTATE_TYPE3:
  341. /* connection is already authenticated,
  342. * don't send a header in future requests */
  343. if(*allocuserpwd) {
  344. free(*allocuserpwd);
  345. *allocuserpwd=NULL;
  346. }
  347. authp->done = TRUE;
  348. break;
  349. }
  350. return CURLE_OK;
  351. }
  352. #endif /* USE_NTLM && NTLM_WB_ENABLED */