Project

General

Profile

winspawn.c

Debug calls in winspawn.c - Eugenie Lyzenko, 01/31/2018 09:49 AM

Download (30.4 KB)

 
1
/*
2
** Module   : winspawn.c
3
** Abstract : a simple tool able to spawn a process on an existing user account.
4
**
5
** Copyright (c) 2013-2017, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- --------------------------------Description---------------------------------
8
** 001 MAG 20131130 First version.
9
** 002 MAG 20140307 Added password and no password authentication: uses a secure P2J connection 
10
**                  and temporary credentials to authenticate the request. Redirect STDOUT to a 
11
**                  file for batch clients. 
12
** 003 CA  20140509 Fixed a key length usage in init().
13
** 004 OM  20151204 Removed MaxPermSize as it is was dropped from Java8. Temporarily disable
14
**                  redirect of stdout.
15
** 005 EVL 20160331 Restoring redirect mode for stdout.  Making new console to be hidden.
16
** 006 EVL 20170225 Adding DestroyEnvironmentBlock API call to clean up created resource.  A set
17
**                  of fixes to be able to run spawn.exe on 32-bit Windows.  Replace PathIsRoot
18
**                  usage with PathIsRelative, moving used in _GetUserProfileDirectoryW DWORD
19
**                  variable to the local function variable set. 
20
*/
21
/*
22
** This program is free software: you can redistribute it and/or modify
23
** it under the terms of the GNU Affero General Public License as
24
** published by the Free Software Foundation, either version 3 of the
25
** License, or (at your option) any later version.
26
**
27
** This program is distributed in the hope that it will be useful,
28
** but WITHOUT ANY WARRANTY; without even the implied warranty of
29
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30
** GNU Affero General Public License for more details.
31
**
32
** You may find a copy of the GNU Affero GPL version 3 at the following
33
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
34
** 
35
** Additional terms under GNU Affero GPL version 3 section 7:
36
** 
37
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
38
**   terms apply to the works covered under the License.  These additional terms
39
**   are non-permissive additional terms allowed under Section 7 of the GNU
40
**   Affero GPL version 3 and may not be removed by you.
41
** 
42
**   0. Attribution Requirement.
43
** 
44
**     You must preserve all legal notices or author attributions in the covered
45
**     work or Appropriate Legal Notices displayed by works containing the covered
46
**     work.  You may not remove from the covered work any author or developer
47
**     credit already included within the covered work.
48
** 
49
**   1. No License To Use Trademarks.
50
** 
51
**     This license does not grant any license or rights to use the trademarks
52
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
53
**     of Golden Code Development Corporation. You are not authorized to use the
54
**     name Golden Code, FWD, or the names of any author or contributor, for
55
**     publicity purposes without written authorization.
56
** 
57
**   2. No Misrepresentation of Affiliation.
58
** 
59
**     You may not represent yourself as Golden Code Development Corporation or FWD.
60
** 
61
**     You may not represent yourself for publicity purposes as associated with
62
**     Golden Code Development Corporation, FWD, or any author or contributor to
63
**     the covered work, without written authorization.
64
** 
65
**   3. No Misrepresentation of Source or Origin.
66
** 
67
**     You may not represent the covered work as solely your work.  All modified
68
**     versions of the covered work must be marked in a reasonable way to make it
69
**     clear that the modified work is not originating from Golden Code Development
70
**     Corporation or FWD.  All modified versions must contain the notices of
71
**     attribution required in this license.
72
*/
73

    
74
#define _WIN32_WINNT 0x0500
75

    
76
#include <stdio.h>
77
#include <stdlib.h>
78
#include <wchar.h>
79
#include <jni.h>
80

    
81
#include <Windows.h>
82

    
83
#define EXIT_SUCCESS 0
84

    
85
#define ERR_SPAWN_NO_ARGS            -1
86
#define ERR_SPAWN_LOGON_USER         -2
87
#define ERR_SPAWN_CREATE_ENVIRONMENT -3
88
#define ERR_SPAWN_GET_USER_PROFILE   -4
89
#define ERR_SPAWN_WORKING_DIRECTORY  -5
90
#define ERR_SPAWN_CREATE_PROCESS     -6
91
#define ERR_SPAWN_LOAD_LIBRARIES     -7
92

    
93
#define ERR_JNI_JVM_CREATE             -100
94
#define ERR_JNI_P2J_ENTRY_POINT_CLASS  -101
95
#define ERR_JNI_P2J_ENTRY_POINT_METHOD -102
96
#define ERR_JNI_REMOTE_SERVER_PORT     -103
97
#define ERR_JNI_REMOTE_SERVER_HOST     -104
98
#define ERR_JNI_REMOTE_SERVER_ALIAS    -105
99
#define ERR_JNI_REMOTE_SERVER_UUID     -106
100
#define ERR_JNI_REMOTE_COMMAND         -107
101
#define ERR_JNI_JVM_DESTROY            -108
102

    
103
#define DEBUG_JVM 0
104

    
105
#define MIN_ARGS_PASSWORD    4
106
#define MIN_ARGS_NO_PASSWORD 6
107
#define MIN_ARGS_COMMAND     4
108

    
109
#define AUTH_OK           0
110
#define AUTH_ERR_USER     1
111
#define AUTH_ERR_PASSWORD 2
112
#define AUTH_ERR_ROOT     3
113
#define AUTH_ERR_PAM      4
114

    
115
#define MAX_SIZE 4096
116

    
117
#define KEY_SIZE 16
118

    
119
unsigned char key1[KEY_SIZE + 1], key2[KEY_SIZE + 1], key3[KEY_SIZE + 1];
120

    
121
/* STDOUT redirection flag. Default NO redirection */
122
int stdoutRedirect = 0;
123
WCHAR szOutputFile[MAX_SIZE] = L"";
124

    
125
WCHAR szUserProfile[MAX_SIZE] = L"";
126
WCHAR szCommandLine[MAX_SIZE] = L"";
127
WCHAR szPassword[MAX_PATH];
128

    
129
DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE;  // CREATE_NO_WINDOW
130
HANDLE hToken = NULL;
131
LPVOID lpvEnv;
132

    
133
STARTUPINFOW        si = {0};
134
PROCESS_INFORMATION pi = {0};
135

    
136
/* Function prototypes */
137
BOOL PathIsRelativeW(LPWSTR);
138
BOOL PathAppendW(LPWSTR,LPCWSTR);
139
BOOL WINAPI CreateEnvironmentBlock(LPVOID*,HANDLE,BOOL);
140
BOOL WINAPI DestroyEnvironmentBlock(LPVOID);
141
BOOL WINAPI GetUserProfileDirectoryW(HANDLE,LPWSTR,LPDWORD);
142

    
143
/* Function pointer definitions */
144
typedef BOOL (fpPathIsRelativeW)(LPCWSTR);
145
typedef BOOL (fpPathAppendW)(LPWSTR,LPCWSTR);
146
typedef BOOL (fpCreateEnvironmentBlock)(LPVOID*,HANDLE,BOOL);
147
typedef BOOL (fpDestroyEnvironmentBlock)(LPVOID);
148
typedef BOOL (fpGetUserProfileDirectoryW)(HANDLE,LPWSTR,LPDWORD);
149

    
150
/* Function pointer instances */
151
fpPathIsRelativeW* _PathIsRelativeW;
152
fpPathAppendW* _PathAppendW;
153
fpCreateEnvironmentBlock* _CreateEnvironmentBlock;
154
fpDestroyEnvironmentBlock* _DestroyEnvironmentBlock;
155
fpGetUserProfileDirectoryW* _GetUserProfileDirectoryW;
156

    
157
/* Libraries handles */
158
HINSTANCE hDllShlwapi = NULL, hDllUserenv = NULL;
159

    
160
/* spawn ini file */ 
161
static char ini_file[] = ".\\spawn.ini";
162

    
163
/* encryption enabled */
164
static unsigned int enabled = 0;
165

    
166
/**
167
* Initialize. Load configuration from spawn.ini file.
168
* File spawn.ini MUST be stored in the same folder where spawn.exe is located. 
169
*/
170
void init()
171
{
172
   // get enabled option
173
   enabled = GetPrivateProfileInt("Encryption", "enabled", 0, ini_file);
174
   // if enable load keys
175
   if (enabled)
176
   {
177
      // load keys
178
      int nKey1 = GetPrivateProfileString("Encryption", "key1", "", (LPSTR) key1, sizeof(key1), ini_file);
179
      int nKey2 = GetPrivateProfileString("Encryption", "key2", "", (LPSTR) key2, sizeof(key2), ini_file);
180
      int nKey3 = GetPrivateProfileString("Encryption", "key3", "", (LPSTR) key3, sizeof(key3), ini_file);
181
      // check key length
182
      if (nKey1 != KEY_SIZE || nKey2 != KEY_SIZE || nKey3 != KEY_SIZE)
183
      {
184
         enabled = 0;
185
         fwprintf(stderr, L"Key length must be 128 bits.\nFound key1=%d, key2=%d, key3=%d\n", 
186
            nKey1 << 3, nKey2 << 3, nKey3 << 3);
187
      }
188
   }   
189
}
190

    
191
/**
192
* Encode password.
193
*
194
* @param   password
195
*          Password clear-text.
196
*
197
* @param   enc
198
*          Encrypted password.
199
*/
200
void encode(LPSTR password, LPSTR enc)
201
{
202
   int i;
203
   for (i = 0;i < strlen(password);i++)
204
   {
205
      password[i] ^= key1[i % KEY_SIZE];
206
      password[i] ^= key2[i % KEY_SIZE];
207
      password[i] ^= key3[i % KEY_SIZE];
208
      sprintf(&enc[i * 2], "%02X", password[i]);
209
   }
210
}
211

    
212
/**
213
* Decode password.
214
*
215
* @param   password
216
*          Encoded password.
217
*
218
* @param   dec
219
*          Clear text password.
220
*/
221
void decode(LPSTR password, LPSTR dec)
222
{
223
   int i;
224
   for (i = 0;i < strlen(password) / 2;i++)
225
   {
226
      sscanf(&password[i * 2], "%02X", (unsigned int*)&dec[i]);
227
      dec[i] ^= key3[i % KEY_SIZE];
228
      dec[i] ^= key2[i % KEY_SIZE];
229
      dec[i] ^= key1[i % KEY_SIZE];
230
   }   
231
}
232

    
233
/**
234
* Convert UNICODE string to ANSI string.
235
*
236
* @param   szUNICODEString
237
*          UNICODE string to be converted.
238
*
239
* @param   szANSIString
240
*          Output ANSI string. Should have enough length to store the converted string.
241
*
242
* @return  The number of bytes written to the buffer or 0 on failure.
243
*/
244
int UnicodeToAnsi(LPCWSTR szUNICODEString, LPSTR szANSIString)
245
{
246
   return WideCharToMultiByte(CP_ACP,                // ANSI code page
247
                              WC_COMPOSITECHECK,     // Check for accented characters
248
                              szUNICODEString,       // Source Unicode string
249
                              -1,                    // -1 means string is zero-terminated
250
                              szANSIString,          // Destination char string
251
                              MAX_SIZE,              // Size of buffer
252
                              NULL,                  // No default character
253
                              NULL );                // Don't care about this flag
254
}
255

    
256
/**
257
* Convert ANSI string to UNICODE string.
258
*
259
* @param   szANSIString
260
*          ANSI string to be converted.
261
*
262
* @param   szUNICODEString
263
*          Output UNICODE string. Should have enough length to store the converted string.
264
*
265
* @return  The number of bytes written to the buffer or 0 on failure.
266
*/
267
int AnsiToUnicode(LPCSTR szANSIString, LPWSTR szUNICODEString)
268
{
269
   return MultiByteToWideChar(CP_ACP,                    // ANSI code page
270
                              MB_COMPOSITE,              // Check for accented characters
271
                              szANSIString,              // Source ANSI string
272
                              -1,                        // -1 means string is zero-terminated
273
                              szUNICODEString,           // Destination char string
274
                              MAX_SIZE);                 // Size of buffer
275
}
276

    
277
/**
278
* Load Shlwapi.dll
279
*
280
* @return   TRUE  load library success.
281
*           FALSE error loading library.
282
*/
283
BOOL LoadShlwapi()
284
{
285
   hDllShlwapi = LoadLibraryW(L"Shlwapi.dll");
286
   
287
   if (hDllShlwapi)
288
   {
289
      // get functions
290
      _PathIsRelativeW = (fpPathIsRelativeW*) GetProcAddress(hDllShlwapi, "PathIsRelativeW");
291
      _PathAppendW = (fpPathAppendW*) GetProcAddress(hDllShlwapi, "PathAppendW");
292
   }
293
   
294
   return hDllShlwapi != NULL;
295
}
296

    
297
/**
298
* Load Userenv.dll
299
*
300
* @return   TRUE  load library success.
301
*           FALSE error loading library.
302
*/
303
BOOL LoadUserenv()
304
{
305
   hDllUserenv = LoadLibraryW(L"Userenv.dll");
306
   
307
   if (hDllUserenv)
308
   {
309
      // get functions
310
      _CreateEnvironmentBlock = (fpCreateEnvironmentBlock*) 
311
            GetProcAddress(hDllUserenv, "CreateEnvironmentBlock");
312
      _DestroyEnvironmentBlock = (fpDestroyEnvironmentBlock*) 
313
            GetProcAddress(hDllUserenv, "DestroyEnvironmentBlock");
314
      _GetUserProfileDirectoryW = (fpGetUserProfileDirectoryW*) 
315
            GetProcAddress(hDllUserenv, "GetUserProfileDirectoryW");
316
   }
317
   
318
   return hDllUserenv != NULL;
319
}
320

    
321
/**
322
* Clean up.
323
*/
324
void CleanUp()
325
{
326
   /* Close handles */
327
   CloseHandle(hToken);
328
   CloseHandle(pi.hProcess);
329
   CloseHandle(pi.hThread);
330
   CloseHandle(si.hStdOutput);
331
}
332

    
333
/**
334
* Print errors on STDERR.
335
*
336
* @param   code
337
*          Exit code.
338
* @param   method
339
*          Method name where error is throw.
340
*
341
* @return  Exit code
342
*/
343
int audit(int code, LPCWSTR method)
344
{
345
   fwprintf(stderr, L"Error:%d method:%ls\n", code, method);
346
   return code;
347
}
348

    
349
/**
350
* Display error messages from system to STDERR.
351
*
352
* @param   pszAPI
353
*          Name of the API call that generate the error.
354
*/
355
void DisplayError(LPCWSTR pszAPI)
356
{
357
   LPVOID lpvMessageBuffer;
358

    
359
   FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
360
                  NULL,
361
                  GetLastError(),
362
                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
363
                  (LPWSTR)&lpvMessageBuffer,
364
                  0,
365
                  0);
366

    
367
   fwprintf(stderr, L"ERROR: API        = %ls\n", pszAPI);
368
   fwprintf(stderr, L"       error code = %d\n", GetLastError());
369
   fwprintf(stderr, L"       message    = %ls", (LPWSTR)lpvMessageBuffer);
370
   fflush(stderr);
371
   
372
   LocalFree(lpvMessageBuffer);
373
}
374

    
375
/**
376
* Prepare a command line string from arguments.
377
*
378
* @param   argc
379
*          Number of command line arguments.
380
* @param   argv
381
*          Command line arguments.
382
*
383
* @return  Command line string.
384
*/
385
LPWSTR CommandLine(int argc, LPWSTR argv[])
386
{
387
   int i;
388
   for (i = MIN_ARGS_PASSWORD;i < argc;i++)
389
   {
390
      if (i != MIN_ARGS_PASSWORD)
391
      {
392
         wcscat(szCommandLine, L" ");
393
      }
394
      wcscat(szCommandLine, argv[i]);
395
   }
396

    
397
   return szCommandLine;
398
}
399

    
400
/**
401
* Prepare a command line string from arguments.
402
*
403
* @param   argc
404
*          Number of command line arguments.
405
* @param   argv
406
*          Command line arguments.
407
*
408
* @return  Command line string.
409
*/
410
LPWSTR CommandLineFromAnsi(int beg, int end, LPSTR argv[])
411
{
412
   int i;
413
   static WCHAR arg[MAX_SIZE];
414
   
415
   for (i = beg;i < end;i++)
416
   {
417
      if (i != beg)
418
      {
419
         wcscat(szCommandLine, L" ");
420
      }
421
      
422
      AnsiToUnicode(argv[i], arg);
423
      wcscat(szCommandLine, arg);
424
   }
425

    
426
   return szCommandLine;
427
}
428

    
429
/**
430
* Read user password from stdin.
431
*
432
* @return  User password.
433
*/
434
LPWSTR ReadPassword()
435
{
436
   return _getws(szPassword);
437
}
438

    
439
/**
440
* Print help.
441
*
442
* @param   pname
443
*          The program's name.
444
*/
445
int help(LPWSTR pname)
446
{
447
   wprintf(L"Usage:\n");
448
   wprintf(L"- for password authentication: %ls 1 <user> <workdir> <command> [args]\n", pname);
449
   wprintf(L"- for server authentication: %ls 0 <secure-port> <host> <server-alias> <uuid>\n", pname);
450
   
451
   return EXIT_SUCCESS;
452
}
453

    
454
/**
455
* Replace a placeholder within a string.
456
*
457
* @param   string
458
*          Original string.
459
* @param   placeholder
460
*          Placeholder string.
461
* @param   value
462
*          Replacement string value.
463
*
464
* @return  The string after placeholder was replaced.
465
*          If the placeholder is not found returns original string.
466
*/
467
LPWSTR replace_str(LPWSTR string, LPCWSTR placeholder, LPWSTR value)
468
{
469
   static WCHAR buffer[MAX_SIZE];
470
   WCHAR *p;
471
   int len;
472

    
473
   p = wcsstr(string, placeholder);
474
   
475
   if (!p)
476
   {
477
      // placeholder not found
478
      return string;
479
   }
480

    
481
   len = (p - string) / sizeof(WCHAR);
482
   wcsncpy(buffer, string, len); 
483
   buffer[len] = 0;
484

    
485
   swprintf(buffer + len, sizeof(buffer), L"%ls%ls", value, p + wcslen(placeholder));
486

    
487
   return buffer;
488
}
489

    
490
/**
491
* Redirect STDOUT to an output file. 
492
*
493
* @param   szWorkingDir
494
*          Working directory.
495
* @param   szOutputFile
496
*          The name of the file where the STDOUT is redirected.
497
*/
498
void stdout_redirect(LPWSTR szWorkingDir, LPWSTR szOutputFile)
499
{
500
   HANDLE hFile;
501
   WCHAR pid[32];
502
   
503
   /* Goto working directory */
504
   if (!SetCurrentDirectoryW(szWorkingDir))
505
   {
506
      DisplayError(L"SetCurrentDirectoryW");
507
   }
508
   
509
   /* convert pid to string */
510
   swprintf(pid, sizeof(pid), L"%lu", GetCurrentProcessId()); 
511
   
512
   /* replace %pid% placeholder */
513
   LPWSTR szFileName = replace_str(szOutputFile, L"%pid%", pid);
514
    
515
   SECURITY_ATTRIBUTES sa;
516
   sa.nLength = sizeof(sa);
517
   sa.lpSecurityDescriptor = NULL;
518
   sa.bInheritHandle = TRUE;   
519
    
520
   /* create file */
521
   hFile = CreateFileW(szFileName,                         // file name 
522
                       FILE_APPEND_DATA,                   // open for writing
523
                       FILE_SHARE_WRITE | FILE_SHARE_READ, // allow multiple readers
524
                       &sa,                                // security
525
                       OPEN_ALWAYS,                        // open or create
526
                       FILE_ATTRIBUTE_NORMAL,              // normal file
527
                       NULL);                              // no template
528
   
529
   /* check for errors */
530
   if (hFile == INVALID_HANDLE_VALUE)
531
   {
532
      DisplayError(L"CreateFileW");
533
   }
534
   else
535
   {
536
      /* redirect */
537
      si.hStdOutput = hFile;
538
      si.dwFlags |= STARTF_USESTDHANDLES;
539
   }
540
}
541

    
542
/**
543
* Spawn a child process on the current logon account.
544
*
545
* @param   szWorkingDir
546
*          Working directory.
547
* @param   szCommand
548
*          Command line.
549
*
550
* @return  Exit status.
551
*/
552
int spawn_nologon(LPWSTR szWorkingDir, LPWSTR szCommand)
553
{
554
   si.cb = sizeof(STARTUPINFO);
555
   // creating hidden console
556
   si.dwFlags |= STARTF_USESHOWWINDOW;
557
   si.wShowWindow = SW_HIDE;
558

    
559
   /* Redirect STDOUT if required */
560
   if (stdoutRedirect)
561
   {
562
      stdout_redirect(szWorkingDir, szOutputFile);
563
   }
564
   
565
   if (!CreateProcessW(NULL,             // application name
566
                       szCommand,        // command line
567
                       NULL,             // process security attributes
568
                       NULL,             // thread security attribute
569
                       TRUE,             // inherit handles
570
                       dwCreationFlags,  // flags
571
                       NULL,             // environment block
572
                       szWorkingDir,     // current directory
573
                       &si,              // startup info
574
                       &pi))             // process info
575
   {
576
      DisplayError(L"CreateProcessW");
577
      return ERR_SPAWN_CREATE_PROCESS;
578
   }
579
   
580
   return EXIT_SUCCESS;
581
}
582

    
583
/**
584
* Spawn a child process on an user account.
585
*
586
* @param   szUser
587
*          User name.
588
* @param   szPswd
589
*          Password.
590
* @param   szWorkingDir
591
*          Working directory.
592
* @param   szCommand
593
*          Command line.
594
*
595
* @return  Exit status.
596
*/
597
int spawn_logon(LPWSTR szUser, LPWSTR szPswd, LPWSTR szWorkingDir, LPWSTR szCommand)
598
{
599
   DWORD dwSize;
600
   
601
   si.cb = sizeof(STARTUPINFO);
602
   // creating hidden console
603
   si.dwFlags |= STARTF_USESHOWWINDOW;
604
   si.wShowWindow = SW_HIDE;
605

    
606
   /* Attempts to log a user on to the local computer. */
607
   if (!LogonUserW(szUser,
608
                   NULL,
609
                   szPswd,
610
                   LOGON32_LOGON_INTERACTIVE,
611
                   LOGON32_PROVIDER_DEFAULT,
612
                   &hToken))
613
   {
614
      DisplayError(L"LogonUser");
615
      return ERR_SPAWN_LOGON_USER;
616
   }
617

    
618
   /* Retrieves the environment variables for the specified user. */
619
   if (!_CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
620
   {
621
      DisplayError(L"CreateEnvironmentBlock");
622
      return ERR_SPAWN_CREATE_ENVIRONMENT;
623
   }
624

    
625
   /* Retrieves the path to the root directory of the specified user's profile. */
626
   dwSize = sizeof(szUserProfile) / sizeof(WCHAR);
627
   if (!_GetUserProfileDirectoryW(hToken, szUserProfile, &dwSize))
628
   {
629
      DisplayError(L"GetUserProfileDirectory");
630
      // clean up enviromnent block on exit
631
      _DestroyEnvironmentBlock(lpvEnv);
632
      return ERR_SPAWN_GET_USER_PROFILE;
633
   }
634

    
635
   /* If relative path to user profile root directory */
636
   if (_PathIsRelativeW(szWorkingDir))
637
   {
638
      if (!_PathAppendW(szUserProfile, szWorkingDir))
639
      {
640
         DisplayError(L"PathAppend");
641
         // clean up enviromnent block on exit
642
         _DestroyEnvironmentBlock(lpvEnv);
643
         return ERR_SPAWN_WORKING_DIRECTORY;
644
      }
645
      szWorkingDir = szUserProfile;
646
   }
647
   
648
   /* Redirect STDOUT if required */
649
   if (stdoutRedirect)
650
   {
651
      stdout_redirect(szWorkingDir, szOutputFile);
652
   }
653

    
654
   fwprintf(stderr, L"Username: %s\n", szUser);
655
   fwprintf(stderr, L"Password: %s\n", szPswd);
656
   fwprintf(stderr, L"Command: %s\n", szCommand);
657
   fwprintf(stderr, L"Working directory: %s\n", szWorkingDir);
658
   
659
   /* Creates a new process and its primary thread.
660
      Then the new process runs the specified executable file in the security context
661
      of the specified credentials (user, domain, and password).
662
      It can optionally load the user profile for a specified user. */
663

    
664
   if (!CreateProcessWithLogonW((LPCWSTR)szUser,       // user name
665
                                NULL,                  // domain
666
                                (LPCWSTR)szPswd,       // password
667
                                LOGON_WITH_PROFILE,    // logon options
668
                                NULL,                  // application name
669
                                (LPWSTR)szCommand,     // command line
670
                                dwCreationFlags,       // flags
671
                                (LPVOID)lpvEnv,        // environment block
672
                                (LPCWSTR)szWorkingDir, // current directory
673
                                &si,                   // startup info
674
                                &pi))                  // process info
675
   {
676
      DisplayError(L"CreateProcessWithLogonW");
677
      // clean up enviromnent block on exit
678
      _DestroyEnvironmentBlock(lpvEnv);
679
      return ERR_SPAWN_CREATE_PROCESS;
680
   }
681

    
682
   // clean up enviromnent block on exit
683
   _DestroyEnvironmentBlock(lpvEnv);
684
   return EXIT_SUCCESS;
685
}
686

    
687
/**
688
* Check if provided user name match current account user name.
689
*
690
* @param   szUserName
691
*          User name.
692
*
693
* @return  1 - Account user name match.
694
*          0 - Account user name does not match.
695
*/
696
int check_user(LPWSTR szUserName)
697
{
698
   WCHAR szAccountUserName[MAX_SIZE]; 
699
   DWORD nSize = MAX_SIZE;
700
   int result = 0;
701
   
702
   if (GetUserNameW(szAccountUserName, &nSize))
703
   {
704
      result = wcscmp(szAccountUserName, szUserName) == 0;
705
      if (!result)
706
      {
707
         fwprintf(stderr, 
708
                  L"User name [%ls] does not match [%ls].\n", 
709
                  szUserName, 
710
                  szAccountUserName);
711
      }
712
   }
713
   else
714
   {   
715
      DisplayError(L"GetUserNameW");
716
   }
717
   
718
   return result;
719
}
720

    
721
/**
722
* Launch an in-process JVM and connects to the specified server. Authentication is assumed passed
723
* only if a secure connection can be establiashed with the server and the server's certificate  
724
* matches one from the store.
725
* <p>
726
* If authentication passes, starts a new process with the real command.
727
*
728
* @param   cport
729
*          The P2J server's port.
730
* @param   chost
731
*          The P2J server's host.
732
* @param   calias
733
*          The certificate alias for the P2J server.
734
* @param   uuid
735
*          Unique identifier for this request; must be known by the target P2J server.
736
*/
737
int launchP2JClient(LPSTR cport, LPSTR chost, LPSTR calias, LPSTR cuuid)
738
{
739
   JavaVM *jvm;
740
   JNIEnv *env;
741

    
742
   int nopts;
743
   
744
   JavaVMOption options[9];
745
   options[0].optionString = "-Djava.class.path=./p2j.jar;.";
746
   options[1].optionString = "-Djava.lib.path=.";
747
   options[2].optionString = "-Xmx512m";
748
   options[3].optionString = "-Djava.awt.headless=true";
749
   options[4].optionString = "-Djava.compiler=NONE";
750
   nopts = 5;
751
   
752
   if (DEBUG_JVM)
753
   {
754
      options[5].optionString = "-Xdebug";
755
      options[6].optionString = "-Xnoagent";
756
      options[7].optionString = "-Xrunjdwp:transport=dt_socket,address=2999,server=y,suspend=y";
757
      nopts = 8;
758
   }
759
   
760
   JavaVMInitArgs vm_args; /* VM initialization arguments */
761
   vm_args.version   = JNI_VERSION_1_6;
762
   vm_args.nOptions  = nopts;
763
   vm_args.options   = options;
764
   vm_args.ignoreUnrecognized = 0;
765

    
766
   jint err = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
767
   if (err != JNI_OK)
768
   {
769
      return(audit(ERR_JNI_JVM_CREATE, L"JNI_CreateJavaVM"));
770
   }
771

    
772
   jclass cls = (*env)->FindClass(env, "com/goldencode/p2j/main/NativeSecureConnection");
773
   if (cls == NULL)
774
   {
775
      (*jvm)->DestroyJavaVM(jvm);
776

    
777
      return(audit(ERR_JNI_P2J_ENTRY_POINT_CLASS, L"FindClass(NativeSecureConnection)"));
778
   }
779
   
780
   char *signature = "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;";
781
   jmethodID mid = (*env)->GetStaticMethodID(env, cls, "command", signature);
782
   if (mid == NULL)
783
   {
784
      (*jvm)->DestroyJavaVM(jvm);
785

    
786
      return(audit(ERR_JNI_P2J_ENTRY_POINT_METHOD, L"GetStaticMethodID(command)"));
787
   }
788
   
789
   int port = atoi(cport);
790
   
791
   jstring host = (*env)->NewStringUTF(env, chost);
792
   if (host == NULL)
793
   {
794
      (*jvm)->DestroyJavaVM(jvm);
795

    
796
      return(audit(ERR_JNI_REMOTE_SERVER_HOST, L"NewStringUTF(host)"));
797
   }
798

    
799
   jstring alias = (*env)->NewStringUTF(env, calias);
800
   if (alias == NULL)
801
   {
802
      (*jvm)->DestroyJavaVM(jvm);
803
      
804
      return(audit(ERR_JNI_REMOTE_SERVER_ALIAS, L"NewStringUTF(alias)"));
805
   }
806

    
807
   jstring uuid = (*env)->NewStringUTF(env, cuuid);
808
   if (uuid == NULL)
809
   {
810
      (*jvm)->DestroyJavaVM(jvm);
811

    
812
      return(audit(ERR_JNI_REMOTE_SERVER_UUID, L"NewStringUTF(uuid)"));
813
   }
814

    
815
   jobjectArray command = (*env)->CallStaticObjectMethod(env, (jclass) cls, (jmethodID) mid,
816
                                                    port, host, alias, uuid);
817
   
818
   if (command == NULL)
819
   {
820
      (*jvm)->DestroyJavaVM(jvm);
821
      
822
      return(audit(ERR_JNI_REMOTE_COMMAND, L"CallStaticObjectMethod(command)"));
823
   }
824

    
825
   /* extract the arguments */
826
   jsize jlength = (*env)->GetArrayLength(env, (jarray) command);
827
   int length = (int) jlength;
828
   
829
   /* make it 1-based, so it follows the structure of the command line arguments 
830
      (0 is program name); also, one more position is needed for execvp, which requires for the
831
      arguments to be null-terminated. */
832
   char **arguments = (char**) malloc((length + 2) * sizeof(char*));
833
   arguments[0] = NULL;
834
   arguments[length + 1] = NULL;
835
   
836
   int i;
837
   for (i = 0; i < (int) length; i++)
838
   {
839
      jstring jarg = (jstring) (*env)->GetObjectArrayElement(env, command, (jsize) i);
840
      jsize argLength = (*env)->GetStringUTFLength(env, jarg);
841
      const char *narg = (*env)->GetStringUTFChars(env, jarg, 0);
842

    
843
      int nlength = (int) argLength + 1;
844
      arguments[i + 1] = (char*) malloc(nlength * sizeof(char));
845
      memset(arguments[i + 1], '\0', nlength);
846
      strcpy(arguments[i + 1], narg);
847

    
848
      (*env)->ReleaseStringUTFChars(env, jarg, narg);
849
   }
850

    
851
   err = (*jvm)->DestroyJavaVM(jvm);
852
   if (err < 0)
853
   {
854
      return(audit(ERR_JNI_JVM_DESTROY, L"DestroyJavaVM"));
855
   }
856
   
857
   if (length < MIN_ARGS_COMMAND)
858
   {
859
      help(L"");
860
      return(ERR_SPAWN_NO_ARGS);
861
   }
862

    
863
   static WCHAR szWorkingDir[MAX_SIZE]; 
864
   static WCHAR szUserName[MAX_SIZE]; 
865
   static WCHAR szPassword[MAX_SIZE]; 
866

    
867
   /* Working directory */
868
   AnsiToUnicode(arguments[MIN_ARGS_COMMAND - 1], szWorkingDir);      
869

    
870
   /* Command line */
871
   LPWSTR szCommand = CommandLineFromAnsi(MIN_ARGS_COMMAND, length + 1, arguments);
872

    
873
   /* User name */
874
   AnsiToUnicode(arguments[1], szUserName);      
875
   
876
   // spawn
877
   if (strcmp(arguments[2], "\"\"") == 0)
878
   {
879
      // check if user name match current user name
880
      if (check_user(szUserName))
881
      {
882
         // spawn on server account
883
         return spawn_nologon(szWorkingDir, szCommand);
884
      }
885
      else
886
      {
887
         return AUTH_ERR_USER;
888
      }
889
   }
890
   else
891
   {
892
      if (enabled)
893
      {
894
         static CHAR password[MAX_SIZE]; 
895
         // decode password
896
         decode(arguments[2], password);
897
         // transform
898
         AnsiToUnicode(password, szPassword);
899
      }
900
      else
901
      {
902
         AnsiToUnicode(arguments[2], szPassword);
903
      }
904
      // spawn on user account
905
      return spawn_logon(szUserName, szPassword, szWorkingDir, szCommand);      
906
   }      
907
}
908

    
909
/**
910
* Main function.
911
* MinGW does not support UNICODE wmain function.
912
* A main function is used as a wrapper for wmain.
913
*
914
* @param   argc
915
*          Number of command line arguments.
916
* @param   argv
917
*          Command line arguments.
918
*
919
* @return  Exit status.
920
*/
921
int wmain(int argc, LPWSTR argv[])
922
{
923
   int result = EXIT_SUCCESS, i;
924

    
925
   /* Load configurations from spawn.ini file */
926
   init();
927
   
928
   /* Print a short help */
929
   if (argc == 1)
930
   {
931
      help(argv[0]);
932
      return(ERR_SPAWN_NO_ARGS);
933
   }
934

    
935
   if (wcscmp(argv[1], L"1") == 0)
936
   {  
937
      /* Check args */
938
      if(argc < MIN_ARGS_PASSWORD)
939
      {
940
         help(argv[0]);
941
         return ERR_SPAWN_NO_ARGS;
942
      }
943

    
944
      /* Get user password */
945
      LPWSTR szPswd = ReadPassword();
946

    
947
      /* Build command line */
948
      LPWSTR szCommand = CommandLine(argc, argv);
949
      
950
      /* Spawn */
951
      result = spawn_logon(argv[2], szPswd, argv[3], szCommand);
952

    
953
      /* Clear password */
954
      SecureZeroMemory(szPswd, wcslen(szPswd));
955
   }
956
   else if (wcscmp(argv[1], L"0") == 0)
957
   {      
958
      /* Could start with no window */
959
      dwCreationFlags |= CREATE_NO_WINDOW;  // flag ignored if the application is is used with CREATE_NEW_CONSOLE.
960

    
961
      if (argc < MIN_ARGS_NO_PASSWORD)
962
      {
963
         help(argv[0]);
964
         return(ERR_SPAWN_NO_ARGS);
965
      }
966
      
967
      /* Search for optional parameters */
968
      for (i = MIN_ARGS_NO_PASSWORD; i < argc; i++)
969
      {
970
         /* -O <outputToFile> */
971
         if ((wcscmp(L"-O", argv[i]) == 0) && (++i < argc))
972
         {
973
            wcscpy(szOutputFile, argv[i]);
974
            stdoutRedirect = 1; // restored redirection mode
975
         }
976
      }
977
            
978
      static CHAR port[MAX_SIZE]; 
979
      static CHAR host[MAX_SIZE]; 
980
      static CHAR alias[MAX_SIZE]; 
981
      static CHAR uuid[MAX_SIZE]; 
982

    
983
      UnicodeToAnsi(argv[2], port);
984
      UnicodeToAnsi(argv[3], host);
985
      UnicodeToAnsi(argv[4], alias);
986
      UnicodeToAnsi(argv[5], uuid);
987
      
988
      result = launchP2JClient(port, host, alias, uuid);
989
   }
990
   else if (wcscmp(argv[1], L"9") == 0)
991
   { 
992
      if (argc == 3)
993
      {
994
         if (enabled)
995
         {
996
            static CHAR password[MAX_SIZE]; 
997
            UnicodeToAnsi(argv[2], password);
998
            // encode password
999
            static CHAR encoded[MAX_SIZE]; 
1000
            encode(password, encoded);
1001
            // print encoded password
1002
            puts(encoded);
1003
         }
1004
         else
1005
         {
1006
            fwprintf(stderr, L"Encryption is disabled.\n");
1007
         }
1008
      }
1009
   }
1010
   else
1011
   {
1012
      help(argv[0]);
1013
      return ERR_SPAWN_NO_ARGS;
1014
   }
1015

    
1016
   return result;
1017
}
1018

    
1019
/**
1020
 * This is for the MinGW compiler which does not support wmain.
1021
 * It is a wrapper for _tmain when _UNICODE is defined (wmain).
1022
 * This wrapper adds ~300 bytes to the program and negligible overhead.
1023
 */
1024

    
1025
#undef _tmain
1026
#ifdef _UNICODE
1027
#define _tmain wmain
1028
#else
1029
#define _tmain main
1030
#endif
1031

    
1032
#if defined(__GNUC__) && defined(_UNICODE)
1033

    
1034
#ifndef __MSVCRT__
1035
#error Unicode main function requires linking to MSVCRT
1036
#endif
1037

    
1038
extern int _CRT_glob;
1039
extern
1040
#ifdef __cplusplus
1041
"C"
1042
#endif
1043
void __wgetmainargs(int*, LPWSTR**, LPWSTR**, int, int*);
1044

    
1045
int main() 
1046
{
1047
   LPWSTR *enpv, *argv;
1048
   int argc, mode, result;
1049
   // this also creates the global variable __wargv
1050
   __wgetmainargs(&argc, &argv, &enpv, _CRT_glob, &mode); 
1051
   /* Load DLL libraries. */
1052
   if (!LoadShlwapi() || !LoadUserenv())
1053
   {
1054
      DisplayError(L"LoadLibraryW");
1055
      return ERR_SPAWN_LOAD_LIBRARIES;
1056
   }
1057
   else
1058
   {
1059
#ifdef MAIN_USE_ENVP
1060
      result = wmain(argc, argv, enpv);
1061
#else
1062
      result = wmain(argc, argv);
1063
#endif
1064
      /* Close handles */
1065
      CleanUp();
1066
      /* Unload Shlwapi.dll */
1067
      if (!hDllShlwapi)
1068
      {
1069
         FreeLibrary(hDllShlwapi);
1070
      }
1071
      /* Unload Userenv.dll */   
1072
      if (!hDllUserenv)
1073
      {
1074
         FreeLibrary(hDllUserenv);
1075
      }
1076
      /* flush log */
1077
      fflush(stderr);
1078
      /* Exit */
1079
      return result;
1080
   }
1081
}
1082

    
1083
#endif //defined(__GNUC__) && defined(_UNICODE)