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 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 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 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 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 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 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 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