1 /*
2 * Copyright 2005, 2006 Kai Blin
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 * A dispatcher to run ntlm_auth for wine's sspi module.
19 */
20
21 #include "precomp.h"
22
23 #include <stdio.h>
24 #include <process.h>
25 #include <io.h>
26 #include <errno.h>
27 #include <fcntl.h>
28
29 #ifdef __REACTOS__
30 #define close _close
31 #define read _read
32 #define write _write
33 #endif
34
35 #include <wine/debug.h>
36 WINE_DEFAULT_DEBUG_CHANNEL(ntlm);
37
38 #define INITIAL_BUFFER_SIZE 200
39
flatten_cmdline(const char * prog,char * const argv[])40 char* flatten_cmdline(const char *prog, char* const argv[])
41 {
42 int i;
43 SIZE_T argstr_size = 0;
44 char *argstr, *p;
45
46 /* Compute space needed for the new string, and allocate it */
47 argstr_size += strlen(prog) + 3; // 3 == 2 quotes between 'prog', and 1 space
48 for(i = 0; argv[i] != NULL; ++i)
49 {
50 argstr_size += strlen(argv[i]) + 1; // 1 for space
51 }
52 argstr = HeapAlloc(GetProcessHeap(), 0, (argstr_size + 1) * sizeof(CHAR));
53 if (argstr == NULL)
54 {
55 ERR("ERROR: Not enough memory\n");
56 return NULL;
57 }
58
59 /* Copy the contents and NULL-terminate the string */
60 p = argstr;
61 strcpy(p, "\""); // Open quote
62 strcat(p, prog);
63 strcat(p, "\" "); // Close quote + space
64 p += strlen(p);
65 for(i = 0; argv[i] != NULL; ++i)
66 {
67 strcpy(p, argv[i]);
68 p += strlen(argv[i]);
69 *p++ = ' ';
70 }
71 *p = '\0';
72
73 return argstr;
74 }
75
fork_helper(PNegoHelper * new_helper,const char * prog,char * const argv[])76 SECURITY_STATUS fork_helper(PNegoHelper *new_helper, const char *prog,
77 char* const argv[])
78 {
79 int pipe_in[2];
80 int pipe_out[2];
81 #ifdef __REACTOS__
82 HANDLE hPipe;
83 PROCESS_INFORMATION pi;
84 STARTUPINFOA si;
85 char* cmdline;
86 #endif
87 int i;
88 PNegoHelper helper;
89
90 TRACE("%s ", debugstr_a(prog));
91 for(i = 0; argv[i] != NULL; ++i)
92 {
93 TRACE("%s ", debugstr_a(argv[i]));
94 }
95 TRACE("\n");
96
97 #ifndef __REACTOS__
98
99 #ifdef HAVE_PIPE2
100 if (pipe2( pipe_in, O_CLOEXEC ) < 0 )
101 #endif
102 {
103 if( pipe(pipe_in) < 0 ) return SEC_E_INTERNAL_ERROR;
104 fcntl( pipe_in[0], F_SETFD, FD_CLOEXEC );
105 fcntl( pipe_in[1], F_SETFD, FD_CLOEXEC );
106 }
107 #ifdef HAVE_PIPE2
108 if (pipe2( pipe_out, O_CLOEXEC ) < 0 )
109 #endif
110 {
111 if( pipe(pipe_out) < 0 )
112 {
113 close(pipe_in[0]);
114 close(pipe_in[1]);
115 return SEC_E_INTERNAL_ERROR;
116 }
117 fcntl( pipe_out[0], F_SETFD, FD_CLOEXEC );
118 fcntl( pipe_out[1], F_SETFD, FD_CLOEXEC );
119 }
120
121 #else
122
123 if (_pipe(pipe_in, 0, _O_BINARY /* _O_TEXT */ | _O_NOINHERIT) < 0)
124 {
125 return SEC_E_INTERNAL_ERROR;
126 }
127
128 if (_pipe(pipe_out, 0, _O_BINARY /* _O_TEXT */ | _O_NOINHERIT) < 0)
129 {
130 close(pipe_in[0]);
131 close(pipe_in[1]);
132 return SEC_E_INTERNAL_ERROR;
133 }
134
135 #endif
136
137 if (!(helper = HeapAlloc(GetProcessHeap(),0, sizeof(NegoHelper))))
138 {
139 close(pipe_in[0]);
140 close(pipe_in[1]);
141 close(pipe_out[0]);
142 close(pipe_out[1]);
143 return SEC_E_INSUFFICIENT_MEMORY;
144 }
145
146 #ifndef __REACTOS__
147 helper->helper_pid = fork();
148 #else
149
150 cmdline = flatten_cmdline(prog, argv);
151 if (!cmdline)
152 {
153 close(pipe_in[0]);
154 close(pipe_in[1]);
155 close(pipe_out[0]);
156 close(pipe_out[1]);
157 HeapFree( GetProcessHeap(), 0, helper );
158 return SEC_E_INSUFFICIENT_MEMORY;
159 }
160
161 ZeroMemory(&pi, sizeof(pi));
162 ZeroMemory(&si, sizeof(si));
163 si.cb = sizeof(si);
164
165 si.dwFlags |= STARTF_USESTDHANDLES;
166
167 /* The reading side of the pipe is STDIN for this process */
168 hPipe = (HANDLE)_get_osfhandle(pipe_out[0]);
169 SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
170 si.hStdInput = hPipe;
171
172 /* The writing side of the pipe is STDOUT for this process */
173 hPipe = (HANDLE)_get_osfhandle(pipe_in[1]);
174 SetHandleInformation(hPipe, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
175 si.hStdOutput = hPipe;
176 si.hStdError = hPipe;
177
178 if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
179 {
180 /* We fail just afterwards */
181 helper->helper_pid = (HANDLE)-1;
182 }
183 else
184 {
185 helper->helper_pid = pi.hProcess;
186 CloseHandle(pi.hThread);
187 CloseHandle(pi.hProcess);
188 }
189
190 HeapFree( GetProcessHeap(), 0, cmdline );
191
192 #endif
193
194 #ifndef __REACTOS__
195 if(helper->helper_pid == -1)
196 #else
197 if(helper->helper_pid == (HANDLE)-1)
198 #endif
199 {
200 close(pipe_in[0]);
201 close(pipe_in[1]);
202 close(pipe_out[0]);
203 close(pipe_out[1]);
204 HeapFree( GetProcessHeap(), 0, helper );
205 return SEC_E_INTERNAL_ERROR;
206 }
207
208 #ifndef __REACTOS__
209 if(helper->helper_pid == 0)
210 {
211 /* We're in the child now */
212 dup2(pipe_out[0], 0);
213 close(pipe_out[0]);
214 close(pipe_out[1]);
215
216 dup2(pipe_in[1], 1);
217 close(pipe_in[0]);
218 close(pipe_in[1]);
219
220 execvp(prog, argv);
221
222 /* Whoops, we shouldn't get here. Big badaboom.*/
223 write(STDOUT_FILENO, "BH\n", 3);
224 _exit(1);
225 }
226 else
227 #endif
228 {
229 *new_helper = helper;
230 helper->major = helper->minor = helper->micro = -1;
231 helper->com_buf = NULL;
232 helper->com_buf_size = 0;
233 helper->com_buf_offset = 0;
234 helper->session_key = NULL;
235 helper->neg_flags = 0;
236 helper->crypt.ntlm.a4i = NULL;
237 helper->crypt.ntlm2.send_a4i = NULL;
238 helper->crypt.ntlm2.recv_a4i = NULL;
239 helper->crypt.ntlm2.send_sign_key = NULL;
240 helper->crypt.ntlm2.send_seal_key = NULL;
241 helper->crypt.ntlm2.recv_sign_key = NULL;
242 helper->crypt.ntlm2.recv_seal_key = NULL;
243 helper->pipe_in = pipe_in[0]; // Keep in(read)
244 close(pipe_in[1]); // Close in(write)
245 helper->pipe_out = pipe_out[1]; // Keep out(write)
246 close(pipe_out[0]); // Close out(read)
247 }
248
249 return SEC_E_OK;
250 }
251
read_line(PNegoHelper helper,int * offset_len)252 static SECURITY_STATUS read_line(PNegoHelper helper, int *offset_len)
253 {
254 char *newline;
255 int read_size;
256
257 if(helper->com_buf == NULL)
258 {
259 TRACE("Creating a new buffer for the helper\n");
260 if((helper->com_buf = HeapAlloc(GetProcessHeap(), 0, INITIAL_BUFFER_SIZE)) == NULL)
261 return SEC_E_INSUFFICIENT_MEMORY;
262
263 /* Created a new buffer, size is INITIAL_BUFFER_SIZE, offset is 0 */
264 helper->com_buf_size = INITIAL_BUFFER_SIZE;
265 helper->com_buf_offset = 0;
266 }
267
268 do
269 {
270 TRACE("offset = %d, size = %d\n", helper->com_buf_offset, helper->com_buf_size);
271 if(helper->com_buf_offset + INITIAL_BUFFER_SIZE > helper->com_buf_size)
272 {
273 /* increment buffer size in INITIAL_BUFFER_SIZE steps */
274 char *buf = HeapReAlloc(GetProcessHeap(), 0, helper->com_buf,
275 helper->com_buf_size + INITIAL_BUFFER_SIZE);
276 TRACE("Resizing buffer!\n");
277 if (!buf) return SEC_E_INSUFFICIENT_MEMORY;
278 helper->com_buf_size += INITIAL_BUFFER_SIZE;
279 helper->com_buf = buf;
280 }
281 if((read_size = read(helper->pipe_in, helper->com_buf + helper->com_buf_offset,
282 helper->com_buf_size - helper->com_buf_offset)) <= 0)
283 {
284 return SEC_E_INTERNAL_ERROR;
285 }
286
287 TRACE("read_size = %d, read: %s\n", read_size,
288 debugstr_a(helper->com_buf + helper->com_buf_offset));
289 helper->com_buf_offset += read_size;
290 newline = memchr(helper->com_buf, '\n', helper->com_buf_offset);
291 }while(newline == NULL);
292
293 /* Now, if there's a newline character, and we read more than that newline,
294 * we have to store the offset so we can preserve the additional data.*/
295 if( newline != helper->com_buf + helper->com_buf_offset)
296 {
297 TRACE("offset_len is calculated from %p - %p\n",
298 (helper->com_buf + helper->com_buf_offset), newline+1);
299 /* the length of the offset is the number of chars after the newline */
300 *offset_len = (helper->com_buf + helper->com_buf_offset) - (newline + 1);
301 }
302 else
303 {
304 *offset_len = 0;
305 }
306
307 *newline = '\0';
308
309 return SEC_E_OK;
310 }
311
preserve_unused(PNegoHelper helper,int offset_len)312 static SECURITY_STATUS preserve_unused(PNegoHelper helper, int offset_len)
313 {
314 TRACE("offset_len = %d\n", offset_len);
315
316 if(offset_len > 0)
317 {
318 memmove(helper->com_buf, helper->com_buf + helper->com_buf_offset,
319 offset_len);
320 helper->com_buf_offset = offset_len;
321 }
322 else
323 {
324 helper->com_buf_offset = 0;
325 }
326
327 TRACE("helper->com_buf_offset was set to: %d\n", helper->com_buf_offset);
328 return SEC_E_OK;
329 }
330
run_helper(PNegoHelper helper,char * buffer,unsigned int max_buflen,int * buflen)331 SECURITY_STATUS run_helper(PNegoHelper helper, char *buffer,
332 unsigned int max_buflen, int *buflen)
333 {
334 int offset_len;
335 SECURITY_STATUS sec_status = SEC_E_OK;
336
337 TRACE("In helper: sending %s\n", debugstr_a(buffer));
338
339 /* buffer + '\n' */
340 write(helper->pipe_out, buffer, lstrlenA(buffer));
341 write(helper->pipe_out, "\n", 1);
342
343 if((sec_status = read_line(helper, &offset_len)) != SEC_E_OK)
344 {
345 return sec_status;
346 }
347
348 TRACE("In helper: received %s\n", debugstr_a(helper->com_buf));
349 *buflen = lstrlenA(helper->com_buf);
350
351 if( *buflen > max_buflen)
352 {
353 ERR("Buffer size too small(%d given, %d required) dropping data!\n",
354 max_buflen, *buflen);
355 return SEC_E_BUFFER_TOO_SMALL;
356 }
357
358 if( *buflen < 2 )
359 {
360 return SEC_E_ILLEGAL_MESSAGE;
361 }
362
363 /* We only get ERR if the input size is too big. On a GENSEC error,
364 * ntlm_auth will return BH */
365 if(strncmp(helper->com_buf, "ERR", 3) == 0)
366 {
367 return SEC_E_INVALID_TOKEN;
368 }
369
370 memcpy(buffer, helper->com_buf, *buflen+1);
371
372 sec_status = preserve_unused(helper, offset_len);
373
374 return sec_status;
375 }
376
cleanup_helper(PNegoHelper helper)377 void cleanup_helper(PNegoHelper helper)
378 {
379
380 TRACE("Killing helper %p\n", helper);
381 if(helper == NULL)
382 return;
383
384 HeapFree(GetProcessHeap(), 0, helper->com_buf);
385 HeapFree(GetProcessHeap(), 0, helper->session_key);
386
387 /* closing stdin will terminate ntlm_auth */
388 close(helper->pipe_out);
389 close(helper->pipe_in);
390
391 #ifndef __REACTOS__
392
393 #ifdef HAVE_FORK
394 if (helper->helper_pid > 0) /* reap child */
395 {
396 pid_t wret;
397 do {
398 wret = waitpid(helper->helper_pid, NULL, 0);
399 } while (wret < 0 && errno == EINTR);
400 }
401 #endif
402
403 #endif
404
405 HeapFree(GetProcessHeap(), 0, helper);
406 }
407
check_version(PNegoHelper helper)408 void check_version(PNegoHelper helper)
409 {
410 char temp[80];
411 char *newline;
412 int major = 0, minor = 0, micro = 0, ret;
413
414 TRACE("Checking version of helper\n");
415 if(helper != NULL)
416 {
417 int len = read(helper->pipe_in, temp, sizeof(temp)-1);
418 if (len > 8)
419 {
420 if((newline = memchr(temp, '\n', len)) != NULL)
421 *newline = '\0';
422 else
423 temp[len] = 0;
424
425 TRACE("Exact version is %s\n", debugstr_a(temp));
426 ret = sscanf(temp, "Version %d.%d.%d", &major, &minor, µ);
427 if(ret != 3)
428 {
429 ERR("Failed to get the helper version.\n");
430 helper->major = helper->minor = helper->micro = -1;
431 }
432 else
433 {
434 TRACE("Version recognized: %d.%d.%d\n", major, minor, micro);
435 helper->major = major;
436 helper->minor = minor;
437 helper->micro = micro;
438 }
439 }
440 }
441 }
442