Project

General

Profile

spawn.c

Inproved spawn.c - Eugenie Lyzenko, 04/16/2026 12:59 PM

Download (45.2 KB)

 
1
/*
2
** Module   : spawn.c
3
** Abstract : a simple tool able to spawn a process on an existing user account.
4
**
5
** Copyright (c) 2013-2025, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- --------------------------------Description---------------------------------
8
** 001 MAG 20131127 First version.
9
** 002 MAG 20140205 Fix PTY (pseudo terminal) assignment bug.
10
** 003 CA  20140212 Added no password authentication: uses a secure P2J connection and temporary
11
**                  credentials to authenticate the request.
12
** 004 MAG 20140224 Redirect STDOUT to a file for batch clients.
13
** 005 MAG 20140310 A password argument has been added for agents. The password argument is
14
**                  required only on Windows OS. For Linux / Unix OS this argument is ignored.
15
** 006 ECF 20150907 Removed -XX:MaxPermSize=64m default option, which is no longer supported in
16
**                  Java 8.
17
** 007 EVL 20160217 Adding Solaris 10 u8 customization for ioctl.h to compile.
18
** 008 EVL 20160227 Adding missed getline() replacement.
19
** 009 EVL 20160323 Solaris version does not need the ioctl(fd, TIOCSCTTY,...) to be called.
20
** 010 GES 20160217 Clear the password buffer before freeing the pointer so that it doesn't stay
21
**                  in memory.
22
** 011 EVL 20200120 Added logic to handle valid passwordless user helping to avoid segmentation
23
**                  fault issue.
24
** 012 EVL 20200518 Fixed issue with handling extra debug option for application server spawning.
25
** 013 EVL 20210115 Adding more extra logging to FWD server.log for possible spawn failures.
26
**     EVL 20210119 More debug info to output.
27
**     IAS 20210325 Added addional parameters for command
28
**     EVL 20210519 Fix for segmentation fault in strcmp() library call.  Added new error codes to inform
29
**                  the caller about system errors.
30
** 014 GBB 20230131 outputToFile passed in as arg to create client process command.
31
**                  stdout redirect in Java.
32
** 015 GBB 20230512 The extra debug option removed. Logging levels attached to each message.
33
** 016 GBB 20230523 Cleanup of unused `j` var.
34
** 017 GBB 20230612 Logging the spawner chdir error.
35
** 018 GBB 20230623 Adding "started" print to stderr to notify the server logger to stop listening for logs.
36
** 019 RFB 20230418 Handle when a tilde is passed in as the working directory so it can be expanded
37
**                  appropriately. Ref. #4938.
38
**     RFB 20230421 Added check for existence of the workdir to allow for separation of a missing
39
**                  directory from a permissions issue. Ref. #4938.
40
**     RFB 20230425 Add some robustness to the string handling of added code per review.
41
** 020 GBB 20231213 Adding slf4j-impl.jar to JVM classpath.
42
** 021 EVL 20240117 Getting back the ability to redirect error stream to log inside native code.
43
** 022 CA  20240227 Added aspectjrt.jar to the spawner's classpath.
44
** 023 GBB 20240311 Started keyword removed. Spawner logger to stop listening based on spawn timeout.
45
** 024 GBB 20240729 Replace dependency slf4j-impl with slf4j-api in classpath.
46
** 025 CA  20250221 Added mode '2' to allow check of OS credentials (username/password).
47
** 026 EVL 20250625 Moved working with JVM to get spawn commands into childs process to completely release
48
**                  memory allocated with JVM start JNI command.
49
**     EVL 20250714 Improved shared memory clean up/release after using completed.
50
*/
51

    
52
/*
53
** This program is free software: you can redistribute it and/or modify
54
** it under the terms of the GNU Affero General Public License as
55
** published by the Free Software Foundation, either version 3 of the
56
** License, or (at your option) any later version.
57
**
58
** This program is distributed in the hope that it will be useful,
59
** but WITHOUT ANY WARRANTY; without even the implied warranty of
60
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
61
** GNU Affero General Public License for more details.
62
**
63
** You may find a copy of the GNU Affero GPL version 3 at the following
64
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
65
** 
66
** Additional terms under GNU Affero GPL version 3 section 7:
67
** 
68
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
69
**   terms apply to the works covered under the License.  These additional terms
70
**   are non-permissive additional terms allowed under Section 7 of the GNU
71
**   Affero GPL version 3 and may not be removed by you.
72
** 
73
**   0. Attribution Requirement.
74
** 
75
**     You must preserve all legal notices or author attributions in the covered
76
**     work or Appropriate Legal Notices displayed by works containing the covered
77
**     work.  You may not remove from the covered work any author or developer
78
**     credit already included within the covered work.
79
** 
80
**   1. No License To Use Trademarks.
81
** 
82
**     This license does not grant any license or rights to use the trademarks
83
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
84
**     of Golden Code Development Corporation. You are not authorized to use the
85
**     name Golden Code, FWD, or the names of any author or contributor, for
86
**     publicity purposes without written authorization.
87
** 
88
**   2. No Misrepresentation of Affiliation.
89
** 
90
**     You may not represent yourself as Golden Code Development Corporation or FWD.
91
** 
92
**     You may not represent yourself for publicity purposes as associated with
93
**     Golden Code Development Corporation, FWD, or any author or contributor to
94
**     the covered work, without written authorization.
95
** 
96
**   3. No Misrepresentation of Source or Origin.
97
** 
98
**     You may not represent the covered work as solely your work.  All modified
99
**     versions of the covered work must be marked in a reasonable way to make it
100
**     clear that the modified work is not originating from Golden Code Development
101
**     Corporation or FWD.  All modified versions must contain the notices of
102
**     attribution required in this license.
103
*/
104

    
105
#define _GNU_SOURCE
106
#include <syslog.h>
107
#include <sys/stat.h>
108
#include <sys/types.h>
109
#include <sys/shm.h>
110
#include <sys/ioctl.h>
111
#include <sys/wait.h>
112
#include <unistd.h>
113
#include <string.h>
114
#include <pwd.h>
115
#include <shadow.h>
116
#include <signal.h>
117
#include <grp.h>
118
//#define _XOPEN_SOURCE 600
119
#include <stdio.h>
120
#include <stdlib.h>
121
#include <fcntl.h>
122
#include <errno.h>
123
#include <jni.h>
124

    
125
#ifdef PAM
126
#include <security/pam_appl.h>
127
#define PAM_SERVICE_NAME "common-auth"
128
#else
129
#include <crypt.h>
130
#endif
131

    
132
#define P2J_JAR    "p2j.jar"
133
#define KEY_STORE  "srv-certs.store"
134

    
135
#define MIN_ARGS_CREDS       3
136
#define MIN_ARGS_PASSWORD    4
137
#define MIN_ARGS_NO_PASSWORD 6
138
#define MIN_ARGS_COMMAND     4
139
#define EXIT_SUCCESS         0
140

    
141
#define ERR_SPAWN_NO_ROOT_PERMISSION -1
142
#define ERR_SPAWN_NO_ARGS            -2
143
#define ERR_SPAWN_NO_PASSWORD        -3
144
#define ERR_SPAWN_POSIX_OPENPT       -4
145
#define ERR_SPAWN_GRANPT             -5
146
#define ERR_SPAWN_UNLOCKPT           -6
147
#define ERR_SPAWN_OPEN_SLAVE         -7
148
#define ERR_SPAWN_IOCTL              -8
149
#define ERR_SPAWN_SETGID             -9
150
#define ERR_SPAWN_SETEGID           -10
151
#define ERR_SPAWN_INITGROUPS        -11
152
#define ERR_SPAWN_SETUID            -12
153
#define ERR_SPAWN_SETEUID           -13
154
#define ERR_SPAWN_SETSID            -14
155
#define ERR_SPAWN_CHDIR             -15
156
#define ERR_SPAWN_SETPGID           -16
157
#define ERR_SPAWN_FORK_LEADER       -17
158
#define ERR_SPAWN_FORK_COMMAND      -18
159
#define ERR_SPAWN_CHDIR_WD          -19
160
#define ERR_SPAWN_STAT_WD           -20
161
#define ERR_SPAWN_SHMEM_GET         -21
162
#define ERR_SPAWN_SHMEM_AT          -22
163
#define ERR_SPAWN_SHMEM_DT          -23
164
#define ERR_SPAWN_SHMEM_FREE        -24
165
#define ERR_SPAWN_FORK_SHMEM        -25
166

    
167
#define ERR_JNI_JVM_CREATE             -100
168
#define ERR_JNI_P2J_ENTRY_POINT_CLASS  -101
169
#define ERR_JNI_P2J_ENTRY_POINT_METHOD -102
170
#define ERR_JNI_REMOTE_SERVER_PORT     -103
171
#define ERR_JNI_REMOTE_SERVER_HOST     -104
172
#define ERR_JNI_REMOTE_SERVER_ALIAS    -105
173
#define ERR_JNI_REMOTE_SERVER_UUID     -106
174
#define ERR_JNI_REMOTE_COMMAND         -107
175
#define ERR_JNI_JVM_DESTROY            -108
176
#define ERR_JNI_REMOTE_SERVER_MISC     -109
177

    
178
#define AUTH_OK                        0
179
#define AUTH_ERR_USER                  1
180
#define AUTH_ERR_PASSWORD              2
181
#define AUTH_ERR_ROOT                  3
182
#define AUTH_ERR_PAM                   4
183
#define AUTH_ERR_NULL_ACCOUNT_PASSWORD 5
184
#define AUTH_ERR_CRYPT                 6
185

    
186
#define DEBUG_JVM 0
187

    
188
#define MAX_FILENAME 4096
189
/* Shared memory size, just two 4k pages to have enogh room to keep filename and commands */
190
#define SHM_SIZE 8192
191
#define SHM_PERM_RW_R_NO 0640
192

    
193
#ifdef SOLARIS
194
   #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)/* 07777 */
195
#endif
196

    
197
/* prototypes */
198
void stdout_redirect();
199

    
200
/* Global data */
201
struct passwd *pw;
202
char message_buffer[MAX_FILENAME];
203

    
204
/* File descriptors */
205
int fdm, fds;
206

    
207
/* Output file name */
208
char output_file_name[MAX_FILENAME];
209
int output_to_file_opt_found = 0;
210
/* Native log file name */
211
char log_file_name[MAX_FILENAME];
212
int native_log_found = 0;
213

    
214
/**
215
 * Use syslog to audit the execution of child processes.
216
 *
217
 * @param    code
218
 *           Exit code.
219
 * @param    method
220
 *           Method name where error is throw.
221
 *           
222
 * @return   Exit code
223
 */
224
int audit(int code, const char *method)
225
{
226
   fprintf(stderr, "SEVERE: %d method:%s \n", code, method);
227
   openlog("spawn", LOG_PID, LOG_USER);
228
   syslog(LOG_ERR, "Error:%d method:%s (%m)\n", code, method);
229
   closelog();
230
   return code;
231
}
232

    
233
/**
234
 * Writes message to stderr if extra logging is required.
235
 *
236
 * @param    msg
237
 *           The message to log.
238
 */
239
void logMessage(const char *msg)
240
{
241
    fprintf(stderr, "INFO: %s\n", msg);
242
}
243

    
244
/**
245
 * Assign a controlling terminal to a process.
246
 *
247
 * @return   0 - Success
248
 *           1 - Error
249
 */
250
int assign_pty()
251
{
252
   int result;
253

    
254
   /* Open a pseudo-terminal device */
255
   fdm = posix_openpt(O_RDWR);
256
   if (fdm < 0)
257
   {
258
      return audit(ERR_SPAWN_POSIX_OPENPT, "posix_openpt");
259
   }
260

    
261
   /* Grant access to the slave pseudo-terminal device */
262
   result = grantpt(fdm);
263
   if (result != 0)
264
   {
265
      return audit(ERR_SPAWN_GRANPT, "grantpt");
266
   }
267

    
268
   /*  Unlock a pseudo-terminal master/slave pair */
269
   result = unlockpt(fdm);
270
   if (result != 0)
271
   {
272
      return audit(ERR_SPAWN_UNLOCKPT, "unlockpt");
273
   }
274

    
275
   /* open slave pseudo-terminal device */
276
   fds = open(ptsname(fdm), O_RDWR);
277
   if (fds < 0)
278
   {
279
      return audit(ERR_SPAWN_OPEN_SLAVE, "open(ptsname)");
280
   }
281

    
282
#ifndef SOLARIS
283
   /* Use the slave as TTY */
284
   if (ioctl(fds, TIOCSCTTY, (char *)1) == -1)
285
   {
286
      return audit(ERR_SPAWN_IOCTL, "ioctl");
287
   }
288
#endif
289

    
290
   return EXIT_SUCCESS;
291
}
292

    
293
/**
294
 * Expand the tilde in the given path.
295
 * This should only be called if the first character of the given path
296
 * is a tilde. It will expand that to the environment's HOME directory.
297
 *
298
 * @param    path
299
 *           Path starting with tilde to expand.
300
 * @param    home
301
 *           The home directory.
302
 */
303
void expand_tilde(char *path, char *home)
304
{
305
   size_t homedir_len;
306
   char *homedir = (home != NULL) ? home : getenv("HOME");
307
   homedir_len = strlen(homedir);
308

    
309
   /*
310
    * Relocate the existing path information (starting with the character after the tilde) to the
311
    * same buffer, but leave space for the homedir to be positioned with the memcpy().
312
    * The existing null-terminator will also be relocated to the end of the path buffer.
313
    */
314
   memmove(path + homedir_len, path + 1, strlen(path));
315
   memcpy(path, homedir, homedir_len);
316

    
317
   return;
318
}
319

    
320
/**
321
 * Execute a command on a specified user account.
322
 * A child process is created via fork().
323
 * The child process uid and gid are set as the new user uid and gid.
324
 * Create a new session for child. The child became a session leader.
325
 * Assign a controlling terminal to session leader.
326
 * The child process environment is set from the new user account.
327
 * Create a child process to execute the command.
328
 *
329
 * @param    command
330
 *           Command to execute
331
 * @param    argv
332
 *           Command arguments.
333
 * @param    workdir
334
 *           Process working directory.
335
 *           
336
 * @return   Process exit status.
337
 */
338
int spawn(const char* command, char* const argv[], const char* workdir)
339
{
340
   pid_t child_pid, proc_pid;
341
   int status, result;
342

    
343
   /* Creates a child process */
344
   child_pid = fork();
345

    
346
   if (child_pid == 0)
347
   {
348
      /* Set gid */
349
      if (setgid(pw->pw_gid) < 0  || getgid() != pw->pw_gid)
350
      {
351
         sprintf(message_buffer, "Unable to set GID for %d.", pw->pw_gid);
352
         logMessage(message_buffer);
353
         exit(audit(ERR_SPAWN_SETGID, "setgid"));
354
      }
355

    
356
      /* Set egit*/
357
      if (setegid(pw->pw_gid) < 0 || getegid() != pw->pw_gid)
358
      {
359
         sprintf(message_buffer, "Unable to set EGID for %d.", pw->pw_gid);
360
         logMessage(message_buffer);
361
         exit(audit(ERR_SPAWN_SETEGID, "setegid"));
362
      }
363

    
364
      /* Initialize the supplementary group access list */
365
      if (initgroups(pw->pw_name, pw->pw_gid) < 0)
366
      {
367
         sprintf(message_buffer, "Unable to init groups for PW_NAME %s and GID %d.", pw->pw_name, pw->pw_gid);
368
         logMessage(message_buffer);
369
         exit(audit(ERR_SPAWN_INITGROUPS, "initgroups"));
370
      }
371

    
372
      /* Set uid */
373
      if (setuid(pw->pw_uid) < 0 || getuid() != pw->pw_uid)
374
      {
375
         sprintf(message_buffer, "Unable to set UID for %d.", pw->pw_uid);
376
         logMessage(message_buffer);
377
         exit(audit(ERR_SPAWN_SETUID, "setuid"));
378
      }
379

    
380
      /* Set euid */
381
      if (seteuid(pw->pw_uid) < 0 || geteuid() != pw->pw_uid)
382
      {
383
         sprintf(message_buffer, "Unable to set EUID for %d.", pw->pw_uid);
384
         logMessage(message_buffer);
385
         exit(audit(ERR_SPAWN_SETEUID, "seteuid"));
386
      }
387

    
388
      /* Creates a session and sets the process group ID */
389
      if(setsid() < 0)
390
      {
391
         logMessage("Unable to set SID.");
392
         exit(audit(ERR_SPAWN_SETSID, "setsid"));
393
      }
394

    
395
      /* Assign controlling terminal */
396
      result = assign_pty();
397
      if (result != EXIT_SUCCESS)
398
      {
399
         logMessage("Unable to assign PTY.");
400
         exit(audit(result, "assign_pty"));
401
      }
402

    
403
      /* Set user environment */
404
      char* path = strdup(getenv("PATH"));
405
      setenv("HOME",pw->pw_dir,1);
406
      setenv("USER",pw->pw_name,1);
407
      setenv("LOGNAME",pw->pw_name,1);
408
      setenv("LOGIN",pw->pw_name,1); /*< Historical; only strictly needed on AIX */
409
      setenv("SHELL",pw->pw_shell[0] ? pw->pw_shell : "/bin/sh", 1);
410
      setenv("PATH",path,1);
411
      free(path);
412

    
413
      /* Change the current working directory */
414
      char* chHomeDir = getenv("HOME");
415
      if (chdir(chHomeDir) < 0)
416
      {
417
         if (chdir("/") < 0)
418
         {
419
            sprintf(message_buffer, "Unable to enter the home directory - %s.", chHomeDir);
420
            logMessage(message_buffer);
421
            exit(audit(ERR_SPAWN_CHDIR, "chdir($HOME)"));
422
         }
423
      }
424

    
425
      /* Spawn a child process */
426
      proc_pid = fork();
427

    
428
      if (proc_pid == 0)
429
      {
430
         /* Close master pty */
431
         close(fdm);
432

    
433
         /* 
434
         * Redirect STDIN and STDOUT to PTY. 
435
         * STDERR is always inherited.
436
         */
437
         dup2(fds, STDIN_FILENO); // PTY becomes standard input (0)
438
         dup2(fds, STDOUT_FILENO); // PTY becomes standard output (1)
439
      
440
         /* Cancel certain signals */
441
         signal(SIGCHLD,SIG_DFL); /* A child process dies */
442
         signal(SIGTSTP,SIG_IGN); /* Various TTY signals */
443
         signal(SIGTTOU,SIG_IGN);
444
         signal(SIGTTIN,SIG_IGN);
445
         signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
446
         signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */
447

    
448
         /* Set process group ID */
449
         if (setpgid(0, child_pid) < 0)
450
         {
451
            sprintf(message_buffer, "Forked. Unable to set PGID for %d.", child_pid);
452
            logMessage(message_buffer);
453
            exit(audit(ERR_SPAWN_SETPGID, "setpgid"));
454
         }
455

    
456
         /* Set process working directory */
457
         char *expanded_workdir;
458
         size_t expanded_workdir_len = strlen(workdir) + strlen(pw->pw_dir) + 1;
459
         expanded_workdir = malloc(sizeof(char) * expanded_workdir_len);
460
         strcpy(expanded_workdir, workdir);
461

    
462
         if (expanded_workdir[0] == '~' && expanded_workdir[1] == '/')
463
         {            
464
            expand_tilde(expanded_workdir, pw->pw_dir);
465
         }
466

    
467
         struct stat sb;
468
         if (stat(expanded_workdir, &sb) == 0 && S_ISDIR(sb.st_mode))
469
         {
470
            snprintf(message_buffer, sizeof(message_buffer), "Forked. Setting working directory to %s.",
471
                     expanded_workdir);
472
            logMessage(message_buffer);
473
            if (chdir(expanded_workdir))
474
            {
475
               snprintf(message_buffer, sizeof(message_buffer),
476
                       "Forked. Unable to set working directory to %s. Error: %s, errno is %d",
477
                       expanded_workdir,
478
                       strerror(errno),
479
                       errno);
480
               logMessage(message_buffer);
481
               free(expanded_workdir);
482
               exit(audit(ERR_SPAWN_CHDIR_WD, "chdir(expanded_workdir)"));
483
            }
484
         }
485
         else
486
         {
487
            snprintf(message_buffer, sizeof(message_buffer), "Forked. Working directory (%s) does not exist.",
488
                     expanded_workdir);
489
            logMessage(message_buffer);
490
            free(expanded_workdir);
491
            exit(audit(ERR_SPAWN_STAT_WD, "stat(expanded_workdir)"));
492
         }
493
         free(expanded_workdir);
494
         
495
         /* Redirect STDOUT if required */
496
         if (native_log_found == 1)
497
         {
498
            stdout_redirect();
499
         }
500
         
501
         /* Execute command */
502
         exit(execvp(command, argv));
503
      }
504
      else if (proc_pid < 0)
505
      {
506
         /* On fork() fails */
507
         logMessage("Failure in fork() call.");
508
         exit(audit(ERR_SPAWN_FORK_COMMAND, "fork"));
509
      }
510

    
511
      /* Close slave pty */
512
      close(fds);
513

    
514
      /* Wait for child to exit*/
515
      wait(&status);
516

    
517
      /* Exit */
518
      exit(status);
519
   }
520
   else if (child_pid < 0)
521
   {
522
      /* On fork() fails */
523
      logMessage("Failure in child process creation.");
524
      exit(audit(ERR_SPAWN_FORK_LEADER, "fork"));
525
   }
526

    
527
   return EXIT_SUCCESS;
528
}
529

    
530
#ifdef PAM
531
struct pam_response *reply;
532

    
533
/**
534
 * PAM conversation function.
535
 * This is a minimal implementation.
536
 *
537
 * @param    num_msg
538
 *           The number of messages that are being passed to the function.
539
 * @param    msg
540
 *           A pointer to the buffer that holds the messages from the user.
541
 * @param    resp
542
 *           A pointer to the buffer that holds the responses to the user.
543
 * @param    appdata_ptr
544
 *           Pointer to the application data.
545
 *           
546
 * @return   PAM_SUCCESS      - success.
547
 */
548
int function_conversation(int num_msg,
549
                          const struct pam_message **msg,
550
                          struct pam_response **resp,
551
                          void *appdata_ptr)
552
{
553
   *resp = reply;
554
   return PAM_SUCCESS;
555
}
556

    
557
/**
558
 * PAM authentication.
559
 *
560
 * @param    username
561
 *           User name.
562
 * @param    password
563
 *           Use password.
564
 *           
565
 * @return   AUTH_OK         - success.
566
 *           AUTH_ERR_USER    - user does not exists.
567
 *           AUTH_ERR_PAM     - on other errors.
568
 */
569
int check_user(const char *username, char *password)
570
{
571
   int retval;
572
   const char *user;
573
   pam_handle_t *local_auth_handle = NULL; // this gets set by pam_start
574
   const struct pam_conv local_conversation = { function_conversation, NULL };
575

    
576
   /* The pam_start function creates the PAM context and initiates the PAM transaction. */
577
   retval = pam_start(PAM_SERVICE_NAME, username, &local_conversation, &local_auth_handle);
578

    
579
   if (retval != PAM_SUCCESS)
580
   {
581
      return audit(AUTH_ERR_PAM, "pam_start");
582
   }
583

    
584
   if (password != NULL)
585
   {
586
      reply = (struct pam_response *)malloc(sizeof(struct pam_response));
587
   
588
      reply->resp = password;
589
      reply->resp_retcode = 0;
590
   
591
      /* The pam_authenticate function is used to authenticate the user. */
592
      retval = pam_authenticate(local_auth_handle, 0);
593
   
594
      if (retval != PAM_SUCCESS)
595
      {
596
         switch (retval)
597
         {
598
            case PAM_USER_UNKNOWN:
599
               return audit(AUTH_ERR_USER, "pam_authenticate");
600
            default:
601
               return audit(AUTH_ERR_PAM, "pam_authenticate");
602
         }
603
      }
604
   }
605

    
606
   /* Get mapped user name; PAM may have changed it */
607
   retval = pam_get_item(local_auth_handle, PAM_USER, (const void **)&user);
608
   if (retval != PAM_SUCCESS)
609
   {
610
      return audit(AUTH_ERR_USER, "pam_get_item");
611
   }
612

    
613
   /* Get a pointer to a structure containing the broken-out fields
614
   of the record in the password database (e.g., the local password
615
   file /etc/passwd, NIS, and LDAP) that matches the username name. */
616
   pw = getpwnam(user);
617
   endpwent();
618

    
619
   if (!pw) return audit(AUTH_ERR_USER, "getpwnam"); //user doesn't really exist
620

    
621
   if (pw->pw_uid == 0)
622
   {
623
      return AUTH_ERR_ROOT;   // user is root
624
   }
625

    
626
   /* The pam_end function terminates the PAM transaction and is the
627
      last function an application should call in the PAM context. */
628
   retval = pam_end(local_auth_handle, retval);
629

    
630
   if (retval != PAM_SUCCESS)
631
   {
632
      return audit(AUTH_ERR_PAM, "pam_end");
633
   }
634

    
635
   return AUTH_OK;
636
}
637

    
638
#else
639

    
640
/**
641
 * Get user account informations including uid, gid and password.
642
 * If the user exists check for password match.
643
 * This is basically an authentication action.
644
 * The user root is not allowed.
645
 *
646
 * @param    username
647
 *           User name.
648
 * @param    password
649
 *           Use password.
650
 *           
651
 * @return   AUTH_OK         - success.
652
 *           AUTH_ERR_USER    - user does not exists.
653
 *           AUTH_ERR_PASSWORD - password does not match.
654
 *           AUTH_ERR_ROOT    - user is root.
655
 */
656
int check_user(const char* username, const char* password)
657
{
658
   struct spwd *sp;
659
   char *encrypted, *correct;
660
   int rc;
661

    
662
   sprintf(message_buffer, "Check user account for: %s.", username);
663
   logMessage(message_buffer);
664

    
665
   /* Get a pointer to a structure containing the broken-out fields
666
   of the record in the password database (e.g., the local password
667
   file /etc/passwd, NIS, and LDAP) that matches the username name. */
668
   pw = getpwnam(username);
669
   endpwent();
670

    
671
   if (!pw) return audit(AUTH_ERR_USER, "getpwnam"); //user doesn't really exist
672

    
673
   if (pw->pw_uid == 0)
674
   {
675
      return AUTH_ERR_ROOT;   // user is root
676
   }
677

    
678
   if (password != NULL)
679
   {
680
      logMessage("The password is not null.");
681
      /* Get a pointer to a structure containing the broken-out fields
682
      of the record in the shadow password database that matches the username name. */
683
      sp = getspnam(pw->pw_name);
684
      endspent();
685
   
686
      if (sp)
687
      {
688
         logMessage("GetSPNam not null, used password from structure.");
689
         correct = sp->sp_pwdp;
690
      }
691
      else
692
      {
693
         logMessage("GetSPNam is null, used password from passwd instead.");
694
         correct = pw->pw_passwd;
695
      }
696
      
697
      // here we separate passwordless user from missing password for regular user
698
      if (sp && strlen(password) == 0 && strlen(correct) == 1 && correct[0] == '!')
699
      {
700
         logMessage("Empty password login.");
701
         return AUTH_OK;
702
      }
703

    
704
      // internal error case
705
      if (correct == NULL)
706
      {
707
         sprintf(message_buffer,
708
                 "No password set for user account '%s', cannot compare against non-null input password.",
709
                 username);
710
         logMessage(message_buffer);
711
         return AUTH_ERR_NULL_ACCOUNT_PASSWORD;
712
      }
713

    
714
      encrypted = crypt(password, correct);
715

    
716
      // need to check to avoid NULL pointer in srtcmp()
717
      if (encrypted == NULL)
718
      {
719
         sprintf(message_buffer, "Password encryption error: %s, errno is %d.", strerror(errno), errno);
720
         logMessage(message_buffer);
721
         // code 22 - invalid argument
722
         if (errno == EINVAL)
723
         {
724
            sprintf(message_buffer, "Check the password setup for user '%s'.", username);
725
            logMessage(message_buffer);
726
         }
727
         return AUTH_ERR_CRYPT;
728
      }
729
   
730
      logMessage("Attempt to use login with password.");
731
      // bad pw=2, success=0
732
      rc = strcmp(encrypted, correct);
733

    
734
      sprintf(message_buffer, "Passwords comparison result: %d.", rc);
735
      logMessage(message_buffer);
736

    
737
      return rc ? AUTH_ERR_PASSWORD : AUTH_OK;
738
   }
739
   else
740
   {
741
      logMessage("The password is null.");
742
      return AUTH_OK;
743
   }
744
}
745

    
746
#endif
747

    
748
/**
749
 * Check executable file attributes.
750
 *
751
 * @param    path
752
 *           path to executable file.
753
 * @param    permissions
754
 *           The file's permissions
755
 *           
756
 * @return   0 - OK.
757
 *           1 - No root permissions.
758
 */
759
int check_root(const char *path, int permissions)
760
{
761
   struct stat pstat;
762
   int result;
763

    
764
   result = stat(path, &pstat);
765

    
766
   if (!result)
767
   {
768
      result = (pstat.st_uid == 0) &&
769
               (pstat.st_gid == 0) &&
770
               (pstat.st_mode & ALLPERMS) == permissions ? 0 : 1;
771
   }
772
   else
773
   {
774
      audit(result, "stat");
775
   }
776

    
777
   return result;
778
}
779

    
780
#ifdef SOLARIS
781
#define _BUFSIZE_GET_LINE 1024
782
#define getline get_line
783

    
784
/**
785
* Get line of characters from the stream.
786
*
787
* @param   lineptr
788
*          pointer to the buffer to store the line.
789
* @param   line_size
790
*          the size of the line buffer.
791
* @param   stream
792
*          the stream to read the data from.
793
*
794
* @return  The number of chars read from the stream or -1 if EOF encountered before any chars.
795
*/
796
ssize_t get_line(char **line_ptr, size_t *line_size, FILE *stream)
797
{
798
   // the internal line buffer
799
   char * buff;
800
   // bytes in currently allocated buffer
801
   size_t bytes_allocated = 0;
802
   // current character
803
   int curr_char;
804
   // the length of the chars already being read
805
   size_t llength = 0;
806
   
807
   // check if we need to allocate memory for line buffer
808
   if (*line_ptr == NULL)
809
   {
810
      // need to allocate new buffer
811
      bytes_allocated = _BUFSIZE_GET_LINE;
812
      buff = malloc(sizeof(char) * bytes_allocated);
813
   }
814
   else
815
   {
816
      // buffer already exists
817
      buff = *line_ptr;
818
      bytes_allocated = *line_size;
819
   }
820
   
821
   // read buffer until new line char or EOF
822
   do
823
   {
824
      // get the next character
825
      curr_char = fgetc(stream);
826

    
827
      // end of file - do nothing for char storing
828
      if (curr_char != EOF)
829
      {
830
         // no moe space in a buffer - need to reallocate
831
         if (llength >= bytes_allocated)
832
         {
833
            // new size is old plus buffer size
834
            bytes_allocated += _BUFSIZE_GET_LINE;
835
            buff = realloc(buff, sizeof(char) * bytes_allocated);
836
         }
837
         
838
         // store the next char
839
         *(buff + llength) = (unsigned char)curr_char; 
840
         llength++;
841
      }
842
      
843
   } while (curr_char != '\n' && curr_char != EOF);
844
   
845
   // check the results
846
   if (llength == 0)
847
   {
848
      // buffer was allocated here, need to free memory
849
      if (buff != NULL && *line_ptr == NULL)
850
      {
851
         free(buff);
852
         buff = NULL;
853
      }
854
      // result must be -1 meaning error
855
      llength = -1;
856
      // setting the error value to I/O error
857
      errno = EIO;
858
   }
859
   else
860
   {
861
      // buffer is not empty, set up the NULL terminated string
862
      if (buff != NULL)
863
      {
864
         buff[llength] = '\0';
865
         // return the buffer too including possible new line character
866
         *line_ptr = buff;
867
      }
868
      else
869
      {
870
         // this is pretty impossible case, meeans something is wrong, return -1 meaning error
871
         llength = -1;
872
         // let's consider we have no memory to allocate
873
         errno = ENOMEM;
874
      }
875
   }
876
   // store the bytes required to allocate
877
   *line_size = bytes_allocated;
878
   
879
   return llength;
880
}
881
#endif
882

    
883
/**
884
 * Overwrite the given buffer to erase the contained data and free the pointer.
885
 *
886
 * @param    buf
887
 *           The buffer to zap.
888
 * @param    len
889
 *           The length of the buffer.
890
 */
891
void zap(char** buf, size_t* len)
892
{
893
   if (*buf != NULL)
894
   {
895
      int i;
896
      
897
      int   z   = *len;
898
      char* ptr = *buf;
899
      
900
      for (i = 0; i < z; i++)
901
      {
902
         *(ptr + i) = 0;
903
      }
904
      
905
      free(ptr);
906
      *buf = NULL;
907
   }
908
   
909
   /* make sure our caller doesn't get confused */
910
   *len = 0;
911
}
912

    
913
/**
914
 * Get the user password from stdin.
915
 *
916
 * @param    len
917
 *           On output, this will be set to the length of the returned buffer.
918
 *
919
 * @return   The read password or NULL on error.
920
 */
921
char* password(size_t* len)
922
{
923
   /* by setting these to NULL and 0, getline() will malloc() a buffer */
924
   char* pswd = NULL;
925
   *len = 0;
926

    
927
   /* read a line from the console */
928
   int read = getline(&pswd, len, stdin);
929
   
930
   sprintf(message_buffer, "The password length(data read): %lu(%d).", *len, read);
931
   logMessage(message_buffer);
932

    
933
   if (read == -1)
934
   {
935
      sprintf(message_buffer, "The password read error: %d.", errno);
936
      logMessage(message_buffer);
937
      if (errno == 0)
938
      {
939
         // considering EOF encountered 
940
         if (pswd == NULL)
941
         {
942
            pswd = malloc(1);
943
         }
944
         else
945
         {
946
            pswd = realloc(pswd, 1);
947
         }
948
         pswd[0] = '\0';
949
      }
950
      /* error has occurred, not sure if this can happen but we are being safe */
951
      else if (pswd != NULL)
952
      {
953
         zap(&pswd, len);
954
      }
955
   }
956
   else if (read == 0)
957
   {
958
      if (pswd == NULL)
959
      {
960
         pswd = malloc(1);
961
      }
962
      else
963
      {
964
         pswd = realloc(pswd, 1);
965
      }
966
      pswd[0] = '\0';
967
   }
968
   else if (pswd[read - 1] == '\n')
969
   {
970
      /* remove the newline if it is there */
971
      pswd[read - 1] = 0;
972
   }
973

    
974
   return pswd;
975
}
976

    
977
/**
978
 * Print help.
979
 *
980
 * @param    pname
981
 *           The program's name.
982
 */
983
int help(char *pname)
984
{
985
   printf("Usage:\n");
986
   printf("- for password authentication: %s 1 <user> <workdir> <command> [args]\n", pname);
987
   printf("- for server authentication: %s 0 <secure-port> <host> <server-alias> <uuid>\n", pname);
988
   printf("- for user credentials check: %s 2 <user>\n", pname);
989
   
990
   return EXIT_SUCCESS;
991
}
992

    
993
/**
994
 * Launch an in-process JVM and connects to the specified server. Authentication is assumed passed
995
 * only if a secure connection can be establiashed with the server and the server's certificate  
996
 * matches one from the store.
997
 * <p>         
998
 * If authentication passes, starts a new process with the real command.
999
 *
1000
 * @param    cport
1001
 *           The P2J server's port.
1002
 * @param    chost
1003
 *           The P2J server's host.
1004
 * @param    calias
1005
 *           The certificate alias for the P2J server.
1006
 * @param    uuid
1007
 *           Unique identifier for this request; must be known by the target P2J server.
1008
 */
1009
int launchP2JClient(char cport[], char chost[], char calias[], char cuuid[], char cmisc[])
1010
{
1011
   /* variables to use shared memory in child process to work with JVM */
1012
   pid_t child_pid;
1013
   int status = 0;
1014
   int shmid;
1015

    
1016
   /* prepare shared memory to use inside child process, permissions: rw-r----- */
1017
   shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_PERM_RW_R_NO | IPC_CREAT);
1018
   if (shmid < 0)
1019
   {
1020
      /* On shmget() fails */
1021
      logMessage("Failure in shared memory allocation request.");
1022
      exit(audit(ERR_SPAWN_SHMEM_GET, "shmget"));
1023
   }
1024
   
1025
   /* starting child process to work with JVM */
1026
   child_pid = fork();
1027
   if (child_pid == 0)
1028
   {
1029
      // get shared memory region from starting with full access
1030
      void * sh_data = shmat(shmid, (void *)0, 0);
1031
      if (sh_data == (void *)-1)
1032
      {
1033
         exit(audit(ERR_SPAWN_SHMEM_AT, "shmat in child"));
1034
      }
1035
      /* alias to original address */
1036
      void * sh_data_orig = sh_data;
1037

    
1038
      /* inside child process */
1039
      JavaVM *jvm;
1040
      JNIEnv *env;
1041

    
1042
      int nopts;
1043
   
1044
      JavaVMOption options[8];
1045
      options[0].optionString = "-Djava.class.path=./p2j.jar:./slf4j-api.jar:./aspectjrt.jar:.";
1046
      options[1].optionString = "-Djava.lib.path=.";
1047
      options[2].optionString = "-Xmx512m";
1048
      options[3].optionString = "-Djava.awt.headless=true";
1049
      options[4].optionString = "-Djava.compiler=NONE";
1050
      nopts = 5;
1051
   
1052
      if (DEBUG_JVM)
1053
      {
1054
         options[5].optionString = "-Xdebug";
1055
         options[6].optionString = "-Xnoagent";
1056
         options[7].optionString = "-Xrunjdwp:transport=dt_socket,address=2999,server=y,suspend=y";
1057
         nopts = 8;
1058
      }
1059

    
1060
      JavaVMInitArgs vm_args; /* VM initialization arguments */
1061
      vm_args.version   = JNI_VERSION_1_6;
1062
      vm_args.nOptions  = nopts;
1063
      vm_args.options   = options;
1064
      vm_args.ignoreUnrecognized = 0;
1065

    
1066
      jint err = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
1067
      if (err != JNI_OK)
1068
      {
1069
         exit(audit(ERR_JNI_JVM_CREATE, "JNI_CreateJavaVM"));
1070
      }
1071

    
1072
      jclass cls = (*env)->FindClass(env, "com/goldencode/p2j/main/NativeSecureConnection");
1073
      if (cls == NULL)
1074
      {
1075
         (*jvm)->DestroyJavaVM(jvm);
1076

    
1077
         exit(audit(ERR_JNI_P2J_ENTRY_POINT_CLASS, "FindClass(NativeSecureConnection)"));
1078
      }
1079
   
1080
      char *signature =
1081
            "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;";
1082
      jmethodID mid = (*env)->GetStaticMethodID(env, cls, "command", signature);
1083
      if (mid == NULL)
1084
      {
1085
         (*jvm)->DestroyJavaVM(jvm);
1086

    
1087
         exit(audit(ERR_JNI_P2J_ENTRY_POINT_METHOD, "GetStaticMethodID(command)"));
1088
      }
1089
   
1090
      int port = atoi(cport);
1091
   
1092
      jstring host = (*env)->NewStringUTF(env, chost);
1093
      if (host == NULL)
1094
      {
1095
         (*jvm)->DestroyJavaVM(jvm);
1096

    
1097
         exit(audit(ERR_JNI_REMOTE_SERVER_HOST, "NewStringUTF(host)"));
1098
      }
1099

    
1100
      jstring alias = (*env)->NewStringUTF(env, calias);
1101
      if (alias == NULL)
1102
      {
1103
         (*jvm)->DestroyJavaVM(jvm);
1104
      
1105
         exit(audit(ERR_JNI_REMOTE_SERVER_ALIAS, "NewStringUTF(alias)"));
1106
      }
1107

    
1108
      jstring uuid = (*env)->NewStringUTF(env, cuuid);
1109
      if (uuid == NULL)
1110
      {
1111
         (*jvm)->DestroyJavaVM(jvm);
1112

    
1113
         exit(audit(ERR_JNI_REMOTE_SERVER_UUID, "NewStringUTF(uuid)"));
1114
      }
1115

    
1116
      jstring misc = (*env)->NewStringUTF(env, cmisc);
1117
      if (misc == NULL)
1118
      {
1119
         (*jvm)->DestroyJavaVM(jvm);
1120

    
1121
         exit(audit(ERR_JNI_REMOTE_SERVER_MISC, "NewStringUTF(misc)"));
1122
      }
1123

    
1124
      jobjectArray command = (*env)->CallStaticObjectMethod(env, (jclass) cls, (jmethodID) mid,
1125
                                                            port, host, alias, uuid, misc);
1126
   
1127
      if (command == NULL)
1128
      {
1129
         (*jvm)->DestroyJavaVM(jvm);
1130
      
1131
         exit(audit(ERR_JNI_REMOTE_COMMAND, "CallStaticObjectMethod(command)"));
1132
      }
1133
   
1134
      /* extract the arguments */
1135
      jsize jlength = (*env)->GetArrayLength(env, (jarray) command);
1136
      /* store arguments length */
1137
      memcpy(sh_data, &jlength, sizeof(jsize));
1138
      /* advance pointer to next memory address to write */
1139
      sh_data += sizeof(jsize);
1140

    
1141
      /* store all arguments coming from JVM in shared memory */
1142
      int i;
1143
      for (i = 0; i < (int) jlength; i++)
1144
      {
1145
         jstring jarg = (jstring) (*env)->GetObjectArrayElement(env, command, (jsize) i);
1146
         jsize argLength = (*env)->GetStringUTFLength(env, jarg);
1147
         const char *narg = (*env)->GetStringUTFChars(env, jarg, 0);
1148

    
1149
         if (narg != NULL)
1150
         {
1151
            sprintf(message_buffer, "The argument number %d is: %s.", i, narg);
1152
            logMessage(message_buffer);
1153

    
1154
            int nlength = (int) argLength + 1;
1155
            /* argument length does not include terminated null char */
1156
            memcpy(sh_data, &argLength, sizeof(jsize));
1157
            /* advance pointer to next memory address to write */
1158
            sh_data += sizeof(jsize);
1159
            memset(sh_data, '\0', nlength);
1160
            strcpy(sh_data, narg);
1161
            /* advance pointer to next memory address to write */
1162
            sh_data += nlength;
1163
         }
1164

    
1165
         (*env)->ReleaseStringUTFChars(env, jarg, narg);
1166
      }
1167

    
1168
      /* detach shared memory*/
1169
      if (shmdt(sh_data_orig) == -1)
1170
      {  
1171
         exit(audit(ERR_SPAWN_SHMEM_DT, "shmdt in child"));
1172
      }
1173

    
1174
      err = (*jvm)->DestroyJavaVM(jvm);
1175
      if (err < 0)
1176
      {
1177
         exit(audit(ERR_JNI_JVM_DESTROY, "DestroyJavaVM"));
1178
      }
1179
      
1180
      /* terminate child process */
1181
      exit(EXIT_SUCCESS);
1182
   }
1183
   else if (child_pid < 0)
1184
   {
1185
      /* On fork() fails */
1186
      logMessage("Failure in child process creation to process JVM.");
1187
      exit(audit(ERR_SPAWN_FORK_SHMEM, "fork for JVM"));
1188
   }
1189
      
1190
   /* Wait for child to exit, no need to semaphore usage */
1191
   wait(&status);
1192
      
1193
   /* process shared memory results */
1194
      
1195
   /* get shared memory region */
1196
   void * sh_data = shmat(shmid, (void *)0, SHM_RDONLY);
1197
   if (sh_data == (void *)-1)
1198
   {
1199
      exit(audit(ERR_SPAWN_SHMEM_AT, "shmat in parent"));
1200
   }
1201
   /* alias to original address */
1202
   void * sh_data_orig = sh_data;
1203
      
1204
   int length = 0;
1205
   memcpy(&length, sh_data, sizeof(jsize));
1206
   sh_data += sizeof(jsize);
1207
      
1208
   /* make it 1-based, so it follows the structure of the command line arguments 
1209
      (0 is program name); also, one more position is needed for execvp, which requires for the
1210
      arguments to be null-terminated. */
1211
   char **arguments = (char**) malloc((length + 2 + output_to_file_opt_found) * sizeof(char*));
1212
   arguments[0] = NULL;
1213
   arguments[length + 1 + output_to_file_opt_found] = NULL;
1214

    
1215
   if (output_to_file_opt_found == 1)
1216
   {
1217
      int size = (strlen(output_file_name) + 1) * sizeof(char);
1218
      arguments[length + 1] = (char*) malloc(size);
1219
      memset(arguments[length + 1], '\0', size);
1220
      strcpy(arguments[length + 1], output_file_name);
1221
   }
1222
      
1223
   /* extract argumants from shared memory*/
1224
   int j;
1225
   for (j = 0; j < length; j++)
1226
   {
1227
      int argLen = 0;
1228

    
1229
      /* argument length does not include terminated null char */
1230
      memcpy(&argLen, sh_data, sizeof(jsize));
1231
      sh_data += sizeof(jsize);
1232
         
1233
      int nlen = (int) argLen + 1;
1234
      arguments[j + 1] = (char*) malloc(nlen * sizeof(char));
1235
      memset(arguments[j + 1], '\0', nlen);
1236
      strcpy(arguments[j + 1], sh_data);
1237
      sh_data += nlen;         
1238
   }
1239
      
1240
   /* detach shared memory*/
1241
   if (shmdt(sh_data_orig) == -1)
1242
   {
1243
      exit(audit(ERR_SPAWN_SHMEM_DT, "shmdt in parent"));
1244
   }
1245
   
1246
   /* release OS resources */
1247
   if (shmctl(shmid, IPC_RMID, NULL) == -1)
1248
   {
1249
      exit(audit(ERR_SPAWN_SHMEM_FREE, "shmctl in parent to release shared memory"));      
1250
   }
1251
   
1252
   if (length < MIN_ARGS_COMMAND)
1253
   {
1254
      help("");
1255
      exit(ERR_SPAWN_NO_ARGS);
1256
   }
1257
   else
1258
   {
1259
      /* Check user account credentials */
1260
      int result = check_user(arguments[1], NULL);
1261
   
1262
      switch (result)
1263
      {
1264
         case AUTH_OK:
1265
            sprintf(message_buffer,
1266
                    "Starting with argv[MIN_ARGS_COMMAND]: (%s), argv[MIN_ARGS_COMMAND - 1]: (%s).",
1267
                    arguments[MIN_ARGS_COMMAND],
1268
                    arguments[MIN_ARGS_COMMAND - 1]);
1269
            logMessage(message_buffer);
1270
            result = spawn(arguments[MIN_ARGS_COMMAND],
1271
                           &arguments[MIN_ARGS_COMMAND],
1272
                           arguments[MIN_ARGS_COMMAND - 1]);
1273
            break;
1274
         case AUTH_ERR_USER:
1275
            fputs("SEVERE: Invalid user name.\n", stderr);
1276
            break;
1277
         case AUTH_ERR_PASSWORD:
1278
            fputs("SEVERE: Invalid password.\n", stderr);
1279
            break;
1280
         case AUTH_ERR_ROOT:
1281
            fputs("SEVERE: User root not allowed.\n", stderr);
1282
            break;
1283
         case AUTH_ERR_NULL_ACCOUNT_PASSWORD:
1284
            fputs("SEVERE: No password set for user account.\n", stderr);
1285
            break;
1286
         case AUTH_ERR_CRYPT:
1287
            fputs("SEVERE: Password encryption error.\n", stderr);
1288
            break;
1289
      }
1290
      sprintf(message_buffer, "Spawn result code: (%d).", result);
1291
      logMessage(message_buffer);
1292
      return result;
1293
   }
1294

    
1295
   return EXIT_SUCCESS;
1296
}
1297

    
1298
/**
1299
 * Replace a placeholder within a string.
1300
 *
1301
 * @param    string
1302
 *           Original string.
1303
 * @param    placeholder
1304
 *           Placeholder string.
1305
 * @param    value
1306
 *           Replacement string value.
1307
 *           
1308
 * @return   The string after placeholder was replaced.
1309
 *           If the placeholder is not found returns original string.
1310
 */
1311
char* replace_str(char* string, const char* placeholder, char* value)
1312
{
1313
   static char buffer[MAX_FILENAME];
1314
   int len;
1315
   char *p;
1316

    
1317
   p = strstr(string, placeholder);
1318
   
1319
   if (!p)
1320
   {
1321
      // placeholder not found
1322
      return string;
1323
   }
1324

    
1325
   len = p - string;
1326
   strncpy(buffer, string, len); 
1327
   buffer[len] = 0;
1328

    
1329
   sprintf(buffer + len, "%s%s", value, p + strlen(placeholder));
1330

    
1331
   return buffer;
1332
}
1333

    
1334
/**
1335
 * Redirect STDOUT to an output file. 
1336
 */
1337
void stdout_redirect()
1338
{
1339
   int output_fd; 
1340
   
1341
   /* making output file name with PID */
1342
   sprintf(log_file_name, "spawn_native_%u.log", getpid());
1343
    
1344
   // Open file
1345
   output_fd = open(log_file_name, O_WRONLY | O_CREAT | O_APPEND, 0644);
1346
   
1347
   if (output_fd == -1)
1348
   {
1349
      fprintf(stderr, "Cannot open file %s\n", log_file_name);
1350
   }
1351
   else
1352
   {
1353
      /* Redirect STDOUT to file */
1354
      if (dup2(output_fd, STDOUT_FILENO) == -1)
1355
      {
1356
         close(output_fd);
1357
         fprintf(stderr, "Cannot redirect STDOUT to file %s\n", log_file_name);
1358
      }
1359
   }
1360
}
1361

    
1362
/**
1363
 * Main function which allows both password and P2J server-style authentication.
1364
 * <p>
1365
 * For P2J server-style authentiation, the following files need to exist in current directory:
1366
 * - p2j.jar, which will automatically be added to classpath
1367
 * - srv-certs.store, a key store with the certificates for the valid P2J server which can invoke
1368
 * this utility.
1369
 *
1370
 * Link option: -lm -lcrypt -lpam -ljvm
1371
 * <p>
1372
 * After build do the following:
1373
 *    $ sudo chown root:root spawn
1374
 *    $ sudo chmod 4755 spawn
1375
 *    $ sudo chmod 0440 srv-certs.store
1376
 *    $ sudo chmod 0440 p2j.jar
1377
 *
1378
 * @param    argc
1379
 *           Number of command line arguments.
1380
 * @param    argv
1381
 *           Command line arguments.
1382
 *           
1383
 * @return   Exit status.
1384
 */
1385
int main(int argc, char *argv[])
1386
{
1387
   int result, i;
1388
   char pid[32];
1389
  
1390
   /* convert pid to string */
1391
   sprintf(pid, "%u", getpid()); 
1392
   sprintf(message_buffer, "Spawn process started with pid %s.", pid);
1393
   logMessage(message_buffer);
1394

    
1395
   /* Init output file name buffer */
1396
   output_file_name[0] = 0;
1397
   
1398
   /* Check for root permissions */
1399
   if (check_root(argv[0], 04755))
1400
   {
1401
      fputs("SEVERE: No root access permissions.\n", stderr);
1402
      exit(ERR_SPAWN_NO_ROOT_PERMISSION);
1403
   }
1404

    
1405
   /* Print a short help */
1406
   if (argc == 1)
1407
   {
1408
      help(argv[0]);
1409
      exit(ERR_SPAWN_NO_ARGS);
1410
   }
1411

    
1412
   char *smode = argv[1];
1413
   if (strcmp(smode, "1") == 0 || strcmp(smode, "2") == 0)
1414
   {
1415
      int mode1 = strcmp(smode, "1");
1416
      int mode2 = strcmp(smode, "2");
1417
      
1418
      if ((mode1 == 0 && argc < MIN_ARGS_PASSWORD) || (mode2 == 0 && argc < MIN_ARGS_CREDS))
1419
      {
1420
         help(argv[0]);
1421
         exit(ERR_SPAWN_NO_ARGS);
1422
      }
1423

    
1424
      /* Ask for password */
1425
      size_t len;
1426
      char *psw = password(&len);
1427
   
1428
      if (psw == NULL)
1429
      {
1430
         fputs("SEVERE: No password.\n", stderr);
1431
         exit(ERR_SPAWN_NO_PASSWORD);
1432
      }
1433

    
1434
      /* Check user account credentials */
1435
      result = check_user(argv[2], psw);
1436
      
1437
      /* Password is no longer needed at this point, zero it so it doesn't stay in memory. */
1438
      zap(&psw, &len);
1439

    
1440
      switch (result)
1441
      {
1442
         case AUTH_OK:
1443
            if (mode1 == 0)
1444
            {
1445
               // we need to consider possible one more extra parameter before java arguments
1446
               sprintf(message_buffer,
1447
                       "Spawn cmd (%s) in workdir (%s).",
1448
                       argv[MIN_ARGS_PASSWORD],
1449
                       argv[MIN_ARGS_PASSWORD - 1]);
1450
               logMessage(message_buffer);
1451
               result = spawn(argv[MIN_ARGS_PASSWORD],
1452
                              &argv[MIN_ARGS_PASSWORD],
1453
                              argv[MIN_ARGS_PASSWORD - 1]);
1454
            }
1455
            break;
1456
         case AUTH_ERR_USER:
1457
            fputs("Invalid user name.\n", stderr);
1458
            break;
1459
         case AUTH_ERR_PASSWORD:
1460
            fputs("Invalid password.\n", stderr);
1461
            break;
1462
         case AUTH_ERR_ROOT:
1463
            fputs("User root not allowed.\n", stderr);
1464
            break;
1465
          case AUTH_ERR_NULL_ACCOUNT_PASSWORD:
1466
            fputs("No password set for user account.\n", stderr);
1467
            break;
1468
        case AUTH_ERR_CRYPT:
1469
            fputs("Password encryption error.\n", stderr);
1470
            break;
1471
      }
1472
      sprintf(message_buffer, "Spawn result code: (%d).", result);
1473
      logMessage(message_buffer);
1474
   }
1475
   else if (strcmp(smode, "0") == 0)
1476
   {
1477
      if (check_root(KEY_STORE, 00550))
1478
      {
1479
         fputs("No root access permissions for srv-certs.store.\n", stderr);
1480
         exit(ERR_SPAWN_NO_ROOT_PERMISSION);
1481
      }
1482
      if (check_root(P2J_JAR, 00550))
1483
      {
1484
         fputs("No root access permissions for p2j.jar.\n", stderr);
1485
         exit(ERR_SPAWN_NO_ROOT_PERMISSION);
1486
      }
1487
      
1488
      if (argc < MIN_ARGS_NO_PASSWORD)
1489
      {
1490
         help(argv[0]);
1491
         exit(ERR_SPAWN_NO_ARGS);
1492
      }
1493
      
1494
      /* Search for optional parameters */
1495
      for (i = MIN_ARGS_NO_PASSWORD; i < argc; i++)
1496
      {
1497
         /* -O <outputToFile> */
1498
         if ((strcmp("-O", argv[i]) == 0) && (++i < argc))
1499
         {
1500
            strcpy(output_file_name, argv[i]);
1501
            output_to_file_opt_found = 1;
1502
         }
1503

    
1504
         /* -L switch to mmake native errors logging */
1505
         if (strcmp("-L", argv[i]) == 0)
1506
         {
1507
            native_log_found = 1;
1508
         }
1509
      }
1510

    
1511
      char* misc = (argc < 7) ? "" : argv[6];
1512
      // when the mode is 0 the extra debug option is located after 2-5 parameters so no need
1513
      // to shift indexes for parameters in call below, it is safe
1514
      result = launchP2JClient(argv[2], argv[3], argv[4], argv[5], misc);
1515
   }
1516
   else
1517
   {
1518
      help(argv[0]);
1519
      exit(ERR_SPAWN_NO_ARGS);
1520
   }
1521

    
1522
   exit(result);
1523
}