Line data Source code
1 : /*
2 : exec.c
3 : Created by Danny Goossen, Gioxa Ltd on 4/3/17.
4 :
5 : MIT License
6 :
7 : Copyright (c) 2017 deployctl, Gioxa Ltd.
8 :
9 : Permission is hereby granted, free of charge, to any person obtaining a copy
10 : of this software and associated documentation files (the "Software"), to deal
11 : in the Software without restriction, including without limitation the rights
12 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : copies of the Software, and to permit persons to whom the Software is
14 : furnished to do so, subject to the following conditions:
15 :
16 : The above copyright notice and this permission notice shall be included in all
17 : copies or substantial portions of the Software.
18 :
19 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 : SOFTWARE.
26 :
27 : */
28 :
29 :
30 :
31 : #include "deployd.h"
32 :
33 : #define PIPE_READ 0
34 : #define PIPE_WRITE 1
35 :
36 : struct script_control {
37 : const char *shell; /* shell to be executed */
38 : const char *shellcommand; /* command to be executed */
39 : int master; /* pseudoterminal master file descriptor */
40 : int slave; /* pseudoterminal slave file descriptor */
41 : pid_t child;
42 : #if !HAVE_LIBUTIL
43 : char *line; /* terminal line */
44 : #else
45 : int close_pipe;
46 : #endif
47 : int childstatus; /* child process exit value */
48 : //char ** envp;
49 : cJSON * env_vars;
50 : gid_t gid;
51 : uid_t uid;
52 : int timeout;
53 :
54 : unsigned int
55 : change_ugid:1,
56 : need_envp:1,
57 : die:1; /* terminate program */
58 : };
59 :
60 :
61 92 : int socket_nonblocking (int sock)
62 : {
63 : int flags;
64 :
65 92 : if(sock != INVALID_SOCKET)
66 : {
67 92 : flags = fcntl (sock, F_GETFL, 0);
68 92 : return fcntl (sock, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC | SO_KEEPALIVE);//| O_NDELAY);
69 : }
70 : return -1;
71 :
72 : }
73 : /*
74 : * Set the socket to blocking -rjkaes
75 : */
76 0 : int socket_blocking (int sock)
77 : {
78 : int flags;
79 :
80 0 : if(sock != INVALID_SOCKET)
81 : {
82 0 : flags = fcntl (sock, F_GETFL, 0);
83 0 : return fcntl (sock, F_SETFL, (flags & ~O_NONBLOCK) | O_CLOEXEC);// | O_NDELAY);
84 : }
85 : return -1;
86 : }
87 :
88 0 : int socket_O_CLOEXEC (int sock)
89 : {
90 : int flags;
91 :
92 0 : if(sock != INVALID_SOCKET)
93 : {
94 0 : flags = fcntl (sock, F_GETFL, 0);
95 0 : return fcntl (sock, F_SETFL, flags | O_CLOEXEC);
96 : }
97 : return -1;
98 :
99 : }
100 : /*------------------------------------------------------------------------
101 : * Helper functions for uid and gid
102 : *------------------------------------------------------------------------*/
103 :
104 8 : int get_uid_gid_user(uidguid_t * uidguid,const char * user)
105 : {
106 : struct passwd pwd;
107 : struct passwd *result;
108 : char *buf;
109 : size_t bufsize;
110 : int s;
111 :
112 8 : bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
113 8 : if (bufsize == -1) /* Value was indeterminate */
114 0 : bufsize = 16384; /* Should be more than enough */
115 :
116 8 : buf = malloc(bufsize);
117 8 : if (buf == NULL) { error("malloc pwd\n");return -1;}
118 :
119 8 : s = getpwnam_r(user, &pwd, buf, bufsize, &result);
120 8 : free(buf);
121 8 : if (result == NULL) {
122 2 : if (s == 0) error("user %s not found\n",user);
123 0 : else {errno = s; sock_error("getpwnam_r");}
124 : return 1;
125 : }
126 6 : uidguid->gid=pwd.pw_gid;
127 6 : uidguid->uid=pwd.pw_uid;
128 6 : return 0;
129 : }
130 :
131 4 : int get_gid(gid_t * gid,const char* user)
132 : {
133 : uidguid_t uidguid;
134 4 : int ret=get_uid_gid_user(&uidguid,user);
135 4 : if (!ret)*gid=uidguid.gid;
136 4 : return ret;
137 : }
138 4 : int get_uid(uid_t * uid,const char*user)
139 : {
140 : uidguid_t uidguid;
141 4 : int ret=get_uid_gid_user(&uidguid,user);
142 4 : if (!ret) *uid=uidguid.uid;
143 4 : return ret;
144 : }
145 :
146 : int exec_cmd_col(void * opaque,int cmd);
147 :
148 : /*----------------------------------------------------------------------------------
149 : * int cmd_exec()
150 : * fork and executes the a command as per ((data_exchange_t *)opaque)->paramlist
151 : * and optional environment a per ((data_exchange_t *)opaque)->newenvp
152 : * * with gid and uid as per data_exchange.gid/uid
153 : * with umask according to umask
154 : *
155 : * blocking function with timeout while waiting for feedback and exit-code
156 : *
157 : * output's the cmd stdout and stderr in output_buff and returns exit code for client
158 : *-----------------------------------------------------------------------------------*/
159 51 : int cmd_exec(void * opaque)
160 : {
161 51 : return(exec_cmd_col(opaque,2));
162 : }
163 :
164 : /*----------------------------------------------------------------------------------
165 : * int cmd_write()
166 : * fork and executes a write file command as per
167 : * and optional environment a per ((data_exchange_t *)opaque)->newenvp
168 : * with gid and uid as per data_exchange.gid/uid
169 : * with umask according to umask
170 : *
171 : * blocking function with timeout while waiting for feedback and exit-code
172 : *
173 : * output's the cmd stdout and stderr in output_buff and returns exit code for client
174 : *-----------------------------------------------------------------------------------*/
175 41 : int cmd_write(void * opaque)
176 : {
177 41 : return(exec_cmd_col(opaque,1));
178 : }
179 :
180 : /*----------------------------------------------------------------------------------
181 : * int exec_col(void * opaque,int cmd)
182 : * fork and executes a wcmd_write or cmd_exec according to cmd
183 : * and optional environment a per ((data_exchange_t *)opaque)->newenvp
184 : * with gid and uid as per data_exchange.gid/uid
185 : * with umask according to umask
186 : * blocking function with timeout while waiting for feedback and exit-code
187 : *
188 : * output's the cmd stdout and stderr in output_buff and returns exit code for client
189 : *-----------------------------------------------------------------------------------*/
190 92 : int exec_cmd_col(void * opaque,int cmd_choice)
191 : {
192 92 : debug("start command exec\n");
193 :
194 92 : data_exchange_t * data_exchange=((data_exchange_t *)opaque);
195 : // feedback buffer
196 92 : int exitcode=0;
197 92 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
198 :
199 : // new envp
200 92 : cJSON * env_vars=data_exchange->env_json;
201 :
202 : // get the details for the command
203 92 : char ** paramlist =data_exchange->paramlist;
204 :
205 92 : int needenvp=data_exchange->needenvp;
206 92 : gid_t gid=data_exchange->gid;
207 92 : uid_t uid=data_exchange->uid;
208 :
209 : pid_t child_pid;
210 :
211 : int aStdoutPipe[2];
212 92 : if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");Write_dyn_trace(trace, red,"System error\n");return(15);}
213 : // Fork_it
214 92 : fflush(stdout);
215 92 : child_pid = fork();
216 :
217 184 : if(child_pid == 0)
218 : {
219 : // The Child
220 : // redirect stdout
221 92 : if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {sock_error("redirecting stdout");exit(2);}
222 : // redirect stderr
223 92 : if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
224 :
225 92 : int fd = open("/dev/null", O_WRONLY);
226 92 : if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
227 : // all these are for use by parent only, so close
228 :
229 92 : close(aStdoutPipe[PIPE_READ]);
230 92 : close(aStdoutPipe[PIPE_WRITE]);
231 : // set user to root and group to the requested user
232 :
233 92 : if (!data_exchange->current_user && setgid(gid)) { error("could not set gid\n") ; exit(4);}
234 92 : if (!data_exchange->current_user && setuid(uid)) { error("could not set uid\n") ; exit(5);}
235 :
236 : // user=7 rwx , group=7 r--, other: ---
237 92 : umask( S_IRWXO ); /* set newly created file permissions */
238 :
239 : //printf("%s : %s %s\n", paramlist[0],paramlist[1],paramlist[2]);
240 :
241 92 : if (cmd_choice==1)
242 : {
243 : // write to file
244 41 : FILE *f = fopen(paramlist[1], "w");
245 41 : if (f == NULL){ fprintf(stderr,"Error opening file : %s\n",strerror(errno));exit(8);}
246 : /* print some text */
247 : int retvalue=0;
248 : int errvalue=0;
249 38 : do{ retvalue=fprintf(f, "%s",paramlist[2]);} while ( retvalue==-1 && (errvalue=errno)== EINTR );
250 38 : if (retvalue<0) { fprintf(stderr,"Error io-command %s: %s\n", paramlist[0],strerror(errvalue)); exit (9); }
251 38 : if (fclose(f) <0 ) { fprintf(stderr,"Error io-command %s: %s\n", paramlist[0],strerror(errno));exit (10);}
252 38 : exit(0);
253 : }
254 : // LCOV_EXCL_START
255 : else if (cmd_choice==2)
256 :
257 : // does get tested, but no registration since we never exit here
258 : {
259 : if (needenvp && env_vars && cJSON_GetArraySize(env_vars)>0)
260 : {
261 : cJSON * pos=NULL;
262 : cJSON_ArrayForEach(pos, env_vars)
263 : {
264 : if (cJSON_IsString(pos))
265 : {
266 : setenv(pos->string,pos->valuestring,1);
267 : debug("env: %s=%s\n",pos->string,pos->valuestring);
268 : }
269 : else debug("skipped non string %s\n",pos->string);
270 : }
271 : //cJSON_Delete(env_vars);
272 : //env_vars=NULL;
273 : }
274 :
275 : {
276 : execvp(paramlist[0], paramlist);
277 : //printf("what's wrong here???\n");
278 : }
279 : // just for safety, should never come here
280 : _exit(6);
281 : } else exit(7);
282 : // LCOV_EXCL_STOP
283 : }
284 92 : else if (child_pid>0) // The Parent
285 : {
286 : // parent wait's and return result
287 : // close unused file descriptors, these are for child only
288 :
289 92 : close(aStdoutPipe[PIPE_WRITE]);
290 : char output_buf[0x10000];
291 : fd_set rds;
292 92 : int sfd=aStdoutPipe[PIPE_READ];
293 92 : int s_ret=0;
294 92 : int s_err=0;
295 92 : ssize_t o_read=0;
296 92 : int o_r_error=0;
297 : struct timeval tv;
298 92 : tv.tv_sec = ((data_exchange_t *)opaque)->timeout;
299 : //tv.tv_sec = TIME_OUT_CHILD;
300 92 : tv.tv_usec = 0;
301 92 : int timeout=0;
302 92 : socket_nonblocking(aStdoutPipe[PIPE_READ]);
303 : do
304 : {
305 97 : FD_ZERO( &rds );
306 97 : FD_SET( aStdoutPipe[PIPE_READ], &rds );
307 97 : s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
308 97 : if (s_ret==SOCKET_ERROR) s_err=errno;
309 97 : else if (s_ret>0)
310 : {
311 96 : o_read=read(aStdoutPipe[PIPE_READ], output_buf, 0x10000);
312 96 : if (o_read==0) break;
313 5 : else if (o_read >0)
314 : //Write_dyn_trace(trace, none,"%.*s",o_read,output_buf);
315 5 : Write_dynamic_trace_n (output_buf,o_read,trace);
316 0 : else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
317 : }
318 : else
319 : {
320 1 : Write_dyn_trace(trace, none,"System Error: timeout executing command!!!");
321 1 : exitcode=1;
322 1 : timeout=1;
323 1 : break;
324 : }
325 5 : } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
326 92 : close(aStdoutPipe[PIPE_READ]);
327 92 : usleep(10000);
328 : int status;
329 92 : if (timeout)
330 : {
331 1 : debug("Timeout, kill child\n");
332 1 : kill(child_pid,SIGKILL); // TODO need to check with WHOANG
333 : }
334 : pid_t w=0;
335 : int this_error=0;
336 92 : while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
337 : {
338 0 : debug("exec: interupted Syscall\n");
339 :
340 : }
341 92 : if (w==-1) {
342 0 : sock_error_no("waitpid",this_error);
343 0 : if (!exitcode) Write_dyn_trace(trace, none,"System error: error on 'waitpid()': %s!!!",strerror(this_error));
344 : exitcode=1;
345 : }
346 92 : else if (WIFEXITED(status))
347 : {
348 90 : exitcode=WEXITSTATUS(status);
349 90 : if (exitcode)
350 : {
351 4 : debug("exit cmd: %s w error: %d\n",paramlist[0], exitcode);
352 : }
353 : else
354 : {
355 86 : debug("[OK]\n");
356 : //Write_dyn_trace(trace, green,"\t[OK]\n");
357 : }
358 : }
359 2 : else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
360 2 : alert("command stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
361 2 : exitcode=1;
362 2 : if (!timeout)
363 1 : Write_dyn_trace(trace, red,"\ncommand stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
364 : }
365 : else
366 : { /* Non-standard case -- may never happen */
367 0 : error("Unexpected status child (0x%x)\n", status);exitcode=status;}
368 : }
369 : else
370 : {
371 0 : error("Fork failed \n"); Write_dyn_trace(trace, red,"\nsystem Error FORK failed\n");exitcode=1;}
372 92 : return (exitcode);
373 : }
374 :
375 : /*----------------------------------------------------------------------------------
376 : * ibash_it(char ** output_buf,int buff_len, const char* valuestring ,cJSON * env_vars,int unpriviledged)
377 : * fork and executes a printf of the valuestring
378 : * and optional environment a per env_vars
379 : * with gid and uid as per data_exchange.gid/uid
380 : * with umask according to umask
381 : * blocking function with timeout while waiting for feedback and exit-code
382 : *
383 : * output's the cmd stdout and stderr in output_buff and returns exit code for client
384 : *-----------------------------------------------------------------------------------*/
385 0 : int bash_it(char * output_buf,int buff_len, const char* valuestring ,cJSON * env_vars,int unpriviledged)
386 : {
387 :
388 0 : debug("bash_it: start substitute %s\n",valuestring);
389 : // feedback buffer
390 0 : int v_exit_code=0;
391 0 : int * exitcode=&v_exit_code;
392 :
393 : char shellcommand[1024];
394 0 : snprintf(shellcommand,1024,"/usr/bin/printf \"%s\"",valuestring );
395 0 : printf("start bash_it\n");
396 : pid_t child_pid;
397 :
398 : int aStdoutPipe[2];
399 0 : if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");snprintf(output_buf,buff_len,"System error\n");return(15);}
400 : // Fork_it
401 0 : child_pid = fork();
402 :
403 0 : if(child_pid == 0)
404 : {
405 0 : fflush(stdout);
406 0 : fflush(stderr);
407 :
408 : // No output to STDOUT/STDERR or our result get's compromissed !!!
409 : // NOT even for debug purposes !!!!
410 :
411 0 : if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {sock_error("redirecting stdout");exit(2);}
412 : // redirect stderr
413 : //if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
414 0 : int fd2 = open("/dev/null", O_WRONLY);
415 0 : if (dup2( fd2 , STDERR_FILENO) == -1) {sock_error("redirecting stderr");exit(3);}
416 0 : int fd = open("/dev/null", O_WRONLY);
417 0 : if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
418 : // all these are for use by parent only, so close
419 0 : close(aStdoutPipe[PIPE_READ]);
420 0 : close(aStdoutPipe[PIPE_WRITE]);
421 :
422 : //TODO change to user nobody, if no current user
423 :
424 0 : if (!unpriviledged)
425 : {
426 : uidguid_t uidguid;
427 0 : if (get_uid_gid_user(&uidguid,"nobody")==0)
428 : {
429 0 : if( setgid(uidguid.gid)) { error("could not set gid\n") ; exit(4);}
430 0 : if (setuid(uidguid.uid)) { error("could not set uid\n") ; exit(5);}
431 : }
432 : }
433 0 : if (env_vars && cJSON_GetArraySize(env_vars)>0)
434 : {
435 0 : cJSON * pos=NULL;
436 0 : cJSON_ArrayForEach(pos, env_vars)
437 : {
438 0 : if (cJSON_IsString(pos))
439 : {
440 0 : setenv(pos->string,pos->valuestring,1);
441 : }
442 : }
443 : }
444 0 : const char * the_shell = getenv("SHELL");
445 0 : const char * shname = strrchr(the_shell, '/');
446 0 : if (shname)
447 0 : shname++;
448 : else
449 : shname = the_shell;
450 :
451 0 : if (the_shell == NULL || strcmp(shname,"nologin")==0) the_shell = _PATH_BSHELL;
452 :
453 0 : if (access(the_shell, X_OK) == 0 )
454 : {
455 0 : execl(the_shell, shname, "-c", shellcommand, NULL);
456 : }
457 : else
458 : {
459 0 : execlp(shname, "-c", shellcommand, NULL);
460 : }
461 : // just for safety, should never come here
462 0 : _exit(6);
463 : }
464 0 : else if (child_pid>0) // The Parent
465 : {
466 : // parent wait's and return result
467 : // close unused file descriptors, these are for child only
468 0 : close(aStdoutPipe[PIPE_WRITE]);
469 0 : usleep(100); // wait a bit to get things settled
470 : fd_set rds;
471 0 : int sfd=aStdoutPipe[PIPE_READ];
472 0 : int s_ret=0;
473 0 : int s_err=0;
474 0 : size_t o_read=0;
475 0 : int o_r_error=0;
476 : struct timeval tv;
477 0 : tv.tv_sec = 1;
478 : //tv.tv_sec = TIME_OUT_CHILD;
479 0 : tv.tv_usec = 0;
480 0 : int timeout=0;
481 0 : memset(output_buf,0,buff_len); // clear buffer!!!
482 0 : socket_nonblocking(aStdoutPipe[PIPE_READ]);
483 : do
484 : {
485 0 : FD_ZERO( &rds );
486 0 : FD_SET( aStdoutPipe[PIPE_READ], &rds );
487 0 : s_ret=select(sfd+1, &rds, NULL, NULL, &tv);
488 0 : if (s_ret==SOCKET_ERROR) s_err=errno;
489 0 : else if (s_ret>0)
490 : {
491 0 : size_t len=strlen(output_buf);
492 0 : size_t read_now= buff_len-len-1;
493 0 : if (read_now<1)
494 : {
495 0 : error("bash_it truncating output\n");
496 : char trunck[1024];
497 0 : o_read=read(aStdoutPipe[PIPE_READ], trunck, 1024);
498 : }
499 : else
500 : {
501 0 : char * to_buf=output_buf+len;
502 0 : o_read=read(aStdoutPipe[PIPE_READ], to_buf, read_now);
503 : }
504 0 : if (o_read==0) break;
505 0 : else if (o_read >0);
506 0 : else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("std_out pipe read", o_r_error);break; }
507 : }
508 : else
509 : {
510 0 : sprintf((output_buf),"System Error: timeout substituding commands!!!");
511 0 : *exitcode=1;
512 0 : timeout=1;
513 0 : break;
514 : }
515 0 : } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
516 0 : close(aStdoutPipe[PIPE_READ]);
517 0 : usleep(100);
518 : int status;
519 0 : if (timeout)
520 : {
521 0 : debug("Timeout, kill child\n");
522 0 : kill(child_pid,SIGKILL); // TODO need to check with WHOANG
523 : }
524 : pid_t w=0;
525 : int this_error=0;
526 0 : while ((w= waitpid(child_pid,&status, 0)) ==-1 && (this_error=errno)==EINTR )
527 : {
528 0 : debug("bash_it: interupted Syscall\n");
529 : }
530 0 : if (w==-1) {
531 0 : sock_error_no("waitpid",this_error);
532 0 : if (!*exitcode) sprintf((output_buf),"System error: error on 'waitpid()': %s!!!",strerror(this_error));
533 0 : *exitcode=1;
534 : }
535 0 : else if (WIFEXITED(status))
536 : {
537 0 : *exitcode=WEXITSTATUS(status);
538 0 : if (*exitcode)
539 0 : info("exit cmd: bash_it w error: %d\n", *exitcode);
540 : else
541 : {
542 : //info("[OK]\n");
543 : //sprintf(output_buf+strlen(output_buf),"\t[OK]\n");
544 : ;
545 : }
546 : }
547 0 : else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
548 0 : alert("command stopped/killed by signal %d : %s", WTERMSIG(status),strsignal(WSTOPSIG(status)));
549 0 : *exitcode=1;
550 0 : if (!timeout)
551 0 : sprintf((output_buf),"System Error: Bash stopped/killed by signal %d : %s!!!", WTERMSIG(status),strsignal(WSTOPSIG(status)));
552 : }
553 : else
554 : { /* Non-standard case -- may never happen */
555 0 : error("Unexpected status child (0x%x)\n", status);*exitcode=status;}
556 : }
557 : else
558 : {
559 0 : error("Fork failed \n"); sprintf((output_buf),"System Error: FORK failed!!!");*exitcode=1;}
560 0 : return (*exitcode);
561 : }
562 :
563 : // New PTY concept
564 :
565 0 : static int done(struct script_control *ctl)
566 : {
567 :
568 0 : kill(ctl->child, SIGTERM); /* make sure we don't create orphans */
569 :
570 0 : if (WIFSIGNALED(ctl->childstatus))
571 0 : return(WTERMSIG(ctl->childstatus));
572 : else
573 0 : return(WEXITSTATUS(ctl->childstatus));
574 : }
575 :
576 :
577 0 : static void getmaster(struct script_control *ctl)
578 : {
579 0 : ctl->master=-1;
580 : #if defined(HAVE_LIBUTIL) && defined(HAVE_PTY_H)
581 0 : int rc=-1;
582 0 : rc = openpty(&ctl->master, &ctl->slave, NULL, NULL, NULL);
583 :
584 0 : if (rc < 0) {
585 0 : error("openpty failed");
586 0 : kill(0, SIGTERM);
587 0 : done(ctl);
588 : }
589 : #else
590 : char *pty, *bank, *cp;
591 :
592 : pty = &ctl->line[strlen("/dev/ptyp")];
593 : for (bank = "pqrs"; *bank; bank++) {
594 : int ptypos=(int)strlen("/dev/pty");
595 : ctl->line[ptypos] = *bank;
596 : *pty = '0';
597 : if (access(ctl->line, F_OK) != 0)
598 : break;
599 : for (cp = "0123456789abcdef"; *cp; cp++) {
600 : *pty = *cp;
601 : ctl->master = open(ctl->line, O_RDWR | O_CLOEXEC);
602 : if (ctl->master >= 0) {
603 : char *tp = &ctl->line[strlen("/dev/")];
604 : int ok;
605 :
606 : /* verify slave side is usable */
607 : *tp = 't';
608 : ok = access(ctl->line, R_OK | W_OK) == 0;
609 : *tp = 'p';
610 : if (ok) return;
611 : close(ctl->master);
612 : ctl->master = -1;
613 : }
614 : }
615 : }
616 : ctl->master = -1;
617 : error("out of pty's\n");
618 :
619 : kill(0, SIGTERM);
620 : done(ctl);
621 : #endif // have libutil
622 0 : }
623 :
624 : static void getslave(struct script_control *ctl)
625 : {
626 : #ifndef HAVE_LIBUTIL
627 : ctl->line[strlen("/dev/")] = 't';
628 : ctl->slave = open(ctl->line, O_RDWR | O_CLOEXEC);
629 : if (ctl->slave < 0) {
630 : printf(("cannot open %s"), ctl->line);
631 : kill(0, SIGTERM);
632 : done(ctl);
633 : }
634 : #endif
635 0 : setsid();
636 : // ioctl(ctl->slave, TIOCSCTTY, 0);
637 : }
638 :
639 : /* don't use DBG() stuff here otherwise it will be in the typescript file */
640 0 : static void do_shell(struct script_control *ctl)
641 : {
642 : const char *shname;
643 :
644 0 : getslave(ctl);
645 :
646 : /* close things irrelevant for this process */
647 0 : close(ctl->master);
648 : //close(ctl->sigfd);
649 0 : int fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
650 0 : if (dup2( fd , STDIN_FILENO) == -1) {sock_error("redirecting stdin");exit(3);}
651 : //dup2(ctl->slave, STDIN_FILENO);
652 0 : dup2(ctl->slave, STDOUT_FILENO);
653 0 : dup2(ctl->slave, STDERR_FILENO);
654 0 : close(ctl->slave);
655 :
656 0 : if (ctl->change_ugid && setgid(ctl->gid)) { error("could not set gid\n") ; exit(4);}
657 0 : if (ctl->change_ugid && setuid(ctl->uid)) { error("could not set uid\n") ; exit(5);}
658 :
659 : // user=7 rwx , group=7 r--, other: ---
660 0 : umask( S_IRWXO ); /* set newly created file permissions */
661 :
662 :
663 0 : ctl->master = -1;
664 0 : shname = strrchr(ctl->shell, '/');
665 0 : if (shname)
666 0 : shname++;
667 : else
668 : shname = ctl->shell;
669 :
670 : // TODO check this!! for now disable
671 : //sigprocmask(SIG_SETMASK, &ctl->sigorg, NULL);
672 :
673 : /*
674 : * When invoked from within /etc/csh.login, script spawns a csh shell
675 : * that spawns programs that cannot be killed with a SIGTERM. This is
676 : * because csh has a documented behavior wherein it disables all
677 : * signals when processing the /etc/csh.* files.
678 : *
679 : * Let's restore the default behavior.
680 : */
681 : //signal(SIGTERM, SIG_DFL);
682 0 : setenv("TERM","xterm-256color",1);
683 0 : setenv("CLICOLOR","1",1);
684 : /* test extreem env
685 : int i;
686 : for (i=0;i<150;i++)
687 : {
688 : char name[20];
689 : char value[120];
690 : sprintf(name,"CUSTOM_%d",i);
691 : sprintf(value,"****************************************************************************%d",i);
692 : setenv(name,value,1);
693 : }
694 : */
695 0 : if (ctl->need_envp && ctl->env_vars && cJSON_GetArraySize(ctl->env_vars)>0 )
696 : {
697 0 : cJSON * pos=NULL;
698 0 : cJSON_ArrayForEach(pos, ctl->env_vars)
699 : {
700 0 : if (cJSON_IsString(pos))
701 : {
702 0 : setenv(pos->string,pos->valuestring,1);
703 0 : debug("env: %s=%s\n",pos->string,pos->valuestring);
704 : }
705 0 : else debug("skipped non string %s\n",pos->string);
706 : }
707 : //cJSON_Delete(ctl->env_vars);
708 : //ctl->env_vars=NULL;
709 : }
710 0 : if (access(ctl->shell, X_OK) == 0 )
711 : {
712 : // printf("child: execl\n");
713 0 : execl(ctl->shell, shname, "-c", ctl->shellcommand, NULL);
714 : } else
715 : {
716 : //printf("child: execlp\n");
717 0 : execlp(shname, "-c", ctl->shellcommand, NULL);
718 : }
719 0 : printf(("failed to execute %s\n"), ctl->shell);
720 0 : kill(0, SIGTERM);
721 0 : exit(done(ctl));
722 : }
723 :
724 0 : static int do_io(struct script_control *ctl, struct trace_Struct * trace)
725 : {
726 : // parent wait's and return result
727 : // close unused file descriptors, these are for child only
728 0 : size_t buf_size=0x80000;
729 0 : char * output_buf=calloc(1,buf_size);
730 : fd_set rds;
731 0 : int s_ret=0;
732 0 : int s_err=0;
733 0 : ssize_t o_read=0;
734 0 : int o_r_error=0;
735 : struct timeval tv;
736 0 : tv.tv_sec = ctl->timeout;
737 0 : tv.tv_usec = 0;
738 0 : int timeout=0;
739 0 : int exitcode=0;
740 0 : socket_nonblocking(ctl->master);
741 0 : int maxfds=ctl->master;
742 0 : socket_nonblocking(ctl->master);
743 : #if HAVE_LIBUTIL
744 0 : if (ctl->close_pipe > ctl->master) maxfds=ctl->close_pipe;
745 0 : socket_nonblocking(ctl->close_pipe);
746 : #endif
747 : do
748 : {
749 0 : FD_ZERO( &rds );
750 0 : FD_SET( ctl->master, &rds );
751 : #if HAVE_LIBUTIL
752 0 : FD_SET( ctl->close_pipe, &rds );
753 : #endif
754 0 : tv.tv_sec = ctl->timeout;
755 0 : s_ret=select(maxfds+1, &rds, NULL, NULL, &tv);
756 0 : if (s_ret==SOCKET_ERROR) s_err=errno;
757 0 : else if (s_ret>0)
758 : {
759 0 : if (FD_ISSET(ctl->master, &rds ))
760 : {
761 0 : memset(output_buf,0,buf_size);
762 0 : o_read=read(ctl->master, output_buf,buf_size-1);
763 0 : if (o_read==0) {debug("trace: EOF\n");;break;}
764 0 : else if (o_read >0){
765 0 : debug ("trace: Received %d bytes",(int)o_read);
766 0 : Write_dynamic_trace_n (output_buf,o_read,trace);
767 : }
768 0 : else if ((o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("pty read", o_r_error);break; }
769 : }
770 : #if HAVE_LIBUTIL
771 0 : if (FD_ISSET(ctl->close_pipe, &rds ))
772 : {
773 0 : memset(output_buf,0,buf_size);
774 0 : ssize_t csr=read(ctl->close_pipe,output_buf,buf_size-1);
775 0 : if (csr==0) {debug ("pipeclosed\n");break;}
776 0 : else if (csr<0 && (o_r_error=errno)!=EINTR && o_r_error!=EAGAIN) { sock_error_no("close pipe read", o_r_error);break; }
777 : else break;
778 : }
779 : #endif
780 : }
781 : else
782 : {
783 0 : Write_dyn_trace(trace, red,"System Error: timeout executing command!!!\n");
784 0 : timeout=1;
785 0 : break;
786 : }
787 0 : } while (s_ret!=SOCKET_ERROR || ( s_ret==SOCKET_ERROR && (s_err==EINTR || s_err==EAGAIN || s_err==EWOULDBLOCK)));
788 : //printf("outputbuf p %p\n",output_buf);
789 0 : usleep(10000);
790 : int status;
791 0 : if (output_buf) free(output_buf);
792 0 : output_buf=NULL;
793 0 : if (timeout)
794 : {
795 0 : debug("Timeout, kill child\n");
796 0 : kill(ctl->child,SIGKILL); // TODO need to check with WHOANG
797 : }
798 : pid_t w=0;
799 : int this_error=0;
800 0 : while ((w= waitpid(ctl->child,&status, 0)) ==-1 && (this_error=errno)==EINTR )
801 : {
802 0 : debug("exec: interupted Syscall\n");
803 : }
804 0 : if (w==-1) {
805 0 : sock_error_no("waitpid",this_error);
806 : //if (!exitcode)
807 0 : Write_dyn_trace(trace, red,"System error: error on 'waitpid()': %s!!!\n",strerror(this_error));
808 0 : exitcode=1;
809 : }
810 0 : else if (WIFEXITED(status))
811 : {
812 0 : exitcode=WEXITSTATUS(status);
813 0 : if (exitcode)
814 : {
815 0 : debug("exit cmd: %s w error: %d\n",ctl->shellcommand, exitcode);
816 : }
817 : else
818 : {
819 : //debug("[OK]\n");
820 : //Write_dyn_trace(trace, green,"\t[OK]\n");
821 : }
822 : }
823 0 : else if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
824 0 : alert("command stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
825 0 : exitcode=1;
826 0 : if (!timeout)
827 0 : Write_dyn_trace(trace, red,"\ncommand stopped/killed by signal %d : %s\n", WTERMSIG(status),strsignal(WSTOPSIG(status)));
828 : }
829 : else
830 : { /* Non-standard case -- may never happen */
831 0 : error("Unexpected status child (0x%x)\n", status);
832 0 : exitcode=status;
833 : }
834 :
835 : //restore_tty(ctl, TCSADRAIN);
836 0 : return exitcode;
837 : }
838 :
839 0 : int exec_color(void * opaque)
840 : {
841 0 : debug("start command color exec\n");
842 :
843 0 : data_exchange_t * data_exchange=((data_exchange_t *)opaque);
844 : // feedback buffer
845 0 : int exit_code=0;
846 0 : struct trace_Struct *trace=((data_exchange_t *)opaque)->trace;
847 :
848 :
849 : char line[20];
850 0 : sprintf(line ,"/dev/ptyXX");
851 0 : struct script_control ctl =
852 : {
853 0 : .need_envp=data_exchange->needenvp,
854 0 : .env_vars =data_exchange->env_json,
855 : #if !HAVE_LIBUTIL
856 : .line = line,
857 : #else
858 : .close_pipe= -1,
859 : #endif
860 : .master = -1,
861 0 : .gid=data_exchange->gid,
862 0 : .uid=data_exchange->uid,
863 0 : .timeout=((data_exchange_t *)opaque)->timeout,
864 : 0
865 : };
866 :
867 :
868 0 : const char* shname=NULL;
869 0 : ctl.shell = getenv("SHELL");
870 0 : shname = strrchr(ctl.shell, '/');
871 0 : if (shname)
872 0 : shname++;
873 : else
874 : shname = ctl.shell;
875 0 : if (ctl.shell == NULL || strcmp(shname,"nologin")==0)
876 0 : ctl.shell = _PATH_BSHELL;
877 :
878 0 : getmaster(&ctl);
879 :
880 0 : if (ctl.master==INVALID_SOCKET)
881 : {
882 0 : Write_dyn_trace(trace, red,"\nsystem Error no PTY\n");
883 0 : error("system Error no PTY\n");
884 0 : return (-1);
885 : }
886 0 : ctl.shellcommand=data_exchange->shellcommand;
887 : //strdup("echo -e \"\033[31;1m print it RED \033[0;m \" && sleep 1 && echo -e \"\033[32;1m print it GREEN \033[0;m \" && env && ls -la&& exit 3");
888 : #if HAVE_LIBUTIL
889 : int aStdoutPipe[2];
890 0 : if (pipe(aStdoutPipe) < 0) {sock_error("allocating pipe for child stdout redirect");Write_dyn_trace(trace, red,"System error allocating pipe\n");return(15);}
891 : #endif
892 0 : fflush(stdout);
893 0 : debug("forking\n");
894 0 : ctl.child = fork();
895 0 : switch (ctl.child) {
896 : case -1: /* error */
897 : #if HAVE_LIBUTIL
898 0 : close (aStdoutPipe[0]);
899 0 : close (aStdoutPipe[1]);
900 : #endif
901 0 : error("Fork failed \n");
902 0 : Write_dyn_trace(trace, red,"\nsystem Error FORK failed\n");
903 0 : kill(0, SIGTERM);
904 0 : done(&ctl);
905 0 : exit_code=EXIT_FAILURE;
906 0 : break;
907 : case 0: /* child */
908 : #if HAVE_LIBUTIL
909 0 : close(aStdoutPipe[PIPE_READ]);
910 0 : socket_O_CLOEXEC(aStdoutPipe[PIPE_WRITE]);
911 : #endif
912 0 : printf("is it flushed?\n");
913 0 : fflush(stdout);
914 0 : fflush(stderr);
915 0 : do_shell(&ctl);
916 : break;
917 : default: /* parent */
918 : #if HAVE_LIBUTIL
919 0 : close(aStdoutPipe[PIPE_WRITE]);
920 0 : socket_nonblocking(aStdoutPipe[PIPE_READ]);
921 0 : ctl.close_pipe=aStdoutPipe[PIPE_READ];
922 : #endif
923 0 : exit_code=do_io(&ctl,trace);
924 0 : break;
925 : }
926 0 : close(ctl.master);
927 0 : return exit_code;
928 : }
|