1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2009-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/wait.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <syslog.h>
28
29 #include <mailutils/types.h>
30 #include <mailutils/alloc.h>
31 #include <mailutils/argcv.h>
32 #include <mailutils/wordsplit.h>
33 #include <mailutils/error.h>
34 #include <mailutils/errno.h>
35 #include <mailutils/nls.h>
36 #include <mailutils/stream.h>
37 #include <mailutils/sys/stream.h>
38 #include <mailutils/sys/prog_stream.h>
39 #include <mailutils/util.h>
40
41 static mu_list_t prog_stream_list;
42
43 static int
_prog_stream_register(struct _mu_prog_stream * stream)44 _prog_stream_register (struct _mu_prog_stream *stream)
45 {
46 if (!prog_stream_list)
47 {
48 int rc = mu_list_create (&prog_stream_list);
49 if (rc)
50 return rc;
51 }
52 return mu_list_append (prog_stream_list, stream);
53 }
54
55 static void
_prog_stream_unregister(struct _mu_prog_stream * stream)56 _prog_stream_unregister (struct _mu_prog_stream *stream)
57 {
58 mu_list_remove (prog_stream_list, stream);
59 }
60
61
62
63 #define REDIRECT_STDIN_P(f) ((f) & MU_STREAM_WRITE)
64 #define REDIRECT_STDOUT_P(f) ((f) & MU_STREAM_READ)
65
66 #ifdef RLIMIT_AS
67 # define _MU_RLIMIT_AS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_AS)
68 #else
69 # define _MU_RLIMIT_AS_FLAG 0
70 # define RLIMIT_AS 0
71 #endif
72
73 #ifdef RLIMIT_CPU
74 # define _MU_RLIMIT_CPU_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CPU)
75 #else
76 # define _MU_RLIMIT_CPU_FLAG 0
77 # define RLIMIT_CPU 0
78 #endif
79
80 #ifdef RLIMIT_DATA
81 # define _MU_RLIMIT_DATA_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_DATA)
82 #else
83 # define _MU_RLIMIT_DATA_FLAG 0
84 # define RLIMIT_DATA 0
85 #endif
86
87 #ifdef RLIMIT_FSIZE
88 # define _MU_RLIMIT_FSIZE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_FSIZE)
89 #else
90 # define _MU_RLIMIT_FSIZE_FLAG 0
91 # define RLIMIT_FSIZE 0
92 #endif
93
94 #ifdef RLIMIT_NPROC
95 # define _MU_RLIMIT_NPROC_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NPROC)
96 #else
97 # define _MU_RLIMIT_NPROC_FLAG 0
98 # define RLIMIT_NPROC 0
99 #endif
100
101 #ifdef RLIMIT_CORE
102 # define _MU_RLIMIT_CORE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CORE)
103 #else
104 # define _MU_RLIMIT_CORE_FLAG 0
105 # define RLIMIT_CORE 0
106 #endif
107
108 #ifdef RLIMIT_MEMLOCK
109 # define _MU_RLIMIT_MEMLOCK_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_MEMLOCK)
110 #else
111 # define _MU_RLIMIT_MEMLOCK_FLAG 0
112 # define RLIMIT_MEMLOCK 0
113 #endif
114
115 #ifdef RLIMIT_NOFILE
116 # define _MU_RLIMIT_NOFILE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NOFILE)
117 #else
118 # define _MU_RLIMIT_NOFILE_FLAG 0
119 # define RLIMIT_NOFILE 0
120 #endif
121
122 #ifdef RLIMIT_RSS
123 # define _MU_RLIMIT_RSS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_RSS)
124 #else
125 # define _MU_RLIMIT_RSS_FLAG 0
126 # define RLIMIT_RSS 0
127 #endif
128
129 #ifdef RLIMIT_STACK
130 # define _MU_RLIMIT_STACK MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_STACK)
131 #else
132 # define _MU_RLIMIT_STACK 0
133 # define RLIMIT_STACK 0
134 #endif
135
136 #define _MU_PROG_AVAILABLE_LIMITS \
137 (_MU_RLIMIT_AS_FLAG | \
138 _MU_RLIMIT_CPU_FLAG | \
139 _MU_RLIMIT_DATA_FLAG | \
140 _MU_RLIMIT_FSIZE_FLAG | \
141 _MU_RLIMIT_NPROC_FLAG | \
142 _MU_RLIMIT_CORE_FLAG | \
143 _MU_RLIMIT_MEMLOCK_FLAG | \
144 _MU_RLIMIT_NOFILE_FLAG | \
145 _MU_RLIMIT_RSS_FLAG | \
146 _MU_RLIMIT_STACK)
147
148 int _mu_prog_limit_flags = _MU_PROG_AVAILABLE_LIMITS;
149
150 int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX] = {
151 RLIMIT_AS,
152 RLIMIT_CPU,
153 RLIMIT_DATA,
154 RLIMIT_FSIZE,
155 RLIMIT_NPROC,
156 RLIMIT_CORE,
157 RLIMIT_MEMLOCK,
158 RLIMIT_NOFILE,
159 RLIMIT_RSS,
160 RLIMIT_STACK
161 };
162
163 static int
start_program_filter(int * p,struct _mu_prog_stream * fs,int flags)164 start_program_filter (int *p, struct _mu_prog_stream *fs, int flags)
165 {
166 int rightp[2], leftp[2];
167 int i;
168 int rc = 0;
169
170 if (REDIRECT_STDIN_P (flags))
171 pipe (leftp);
172 if (REDIRECT_STDOUT_P (flags))
173 pipe (rightp);
174
175 switch (fs->pid = fork ())
176 {
177 /* The child branch. */
178 case 0:
179 /* attach the pipes */
180
181 /* Right-end */
182 if (REDIRECT_STDOUT_P (flags))
183 {
184 if (rightp[1] != 1)
185 {
186 close (1);
187 dup2 (rightp[1], 1);
188 }
189 close (rightp[0]);
190 }
191
192 /* Left-end */
193 if (REDIRECT_STDIN_P (flags))
194 {
195 if (leftp[0] != 0)
196 {
197 close (0);
198 dup2 (leftp[0], 0);
199 }
200 close (leftp[1]);
201 }
202
203 if (fs->hint_flags & MU_PROG_HINT_ERRTOOUT)
204 dup2 (1, 2);
205
206 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
207 {
208 if (chdir (fs->hints.mu_prog_workdir))
209 {
210 mu_error (_("cannot change to %s: %s"),
211 fs->hints.mu_prog_workdir, mu_strerror (errno));
212 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
213 _exit (127);
214 }
215 }
216
217 if (fs->hint_flags & MU_PROG_HINT_UID)
218 {
219 if (mu_set_user_privileges (fs->hints.mu_prog_uid,
220 fs->hints.mu_prog_gidv,
221 fs->hints.mu_prog_gidc)
222 && !(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
223 _exit (127);
224 }
225
226 for (i = 0; i < _MU_PROG_LIMIT_MAX; i++)
227 {
228 if (MU_PROG_HINT_LIMIT(i) & fs->hint_flags)
229 {
230 struct rlimit rlim;
231
232 rlim.rlim_cur = rlim.rlim_max = fs->hints.mu_prog_limit[i];
233 if (setrlimit (_mu_prog_limit_codes[i], &rlim))
234 {
235 mu_error (_("error setting limit %d to %lu: %s"),
236 i, (unsigned long) rlim.rlim_cur,
237 mu_strerror (errno));
238 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
239 _exit (127);
240 }
241 }
242 }
243 if (MU_PROG_HINT_PRIO & fs->hint_flags)
244 {
245 if (setpriority (PRIO_PROCESS, 0, fs->hints.mu_prog_prio))
246 {
247 mu_error (_("error setting priority: %s"),
248 mu_strerror (errno));
249 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
250 _exit (127);
251 }
252 }
253
254 /* Close unneded descripitors */
255 mu_close_fds (3);
256
257 /*FIXME: Switch to other uid/gid if desired */
258 execvp (fs->progname, fs->argv);
259
260 /* Report error via syslog */
261 syslog (LOG_ERR|LOG_USER, "can't run %s (ruid=%d, euid=%d): %m",
262 fs->progname, getuid (), geteuid ());
263 _exit (127);
264 /********************/
265
266 /* Parent branches: */
267 case -1:
268 /* Fork has failed */
269 /* Restore things */
270 rc = errno;
271 if (REDIRECT_STDOUT_P (flags))
272 {
273 close (rightp[0]);
274 close (rightp[1]);
275 }
276 if (REDIRECT_STDIN_P (flags))
277 {
278 close (leftp[0]);
279 close (leftp[1]);
280 }
281 break;
282
283 default:
284 if (REDIRECT_STDOUT_P (flags))
285 {
286 p[0] = rightp[0];
287 close (rightp[1]);
288 }
289 else
290 p[0] = -1;
291
292 if (REDIRECT_STDIN_P (flags))
293 {
294 p[1] = leftp[1];
295 close (leftp[0]);
296 }
297 else
298 p[1] = -1;
299 }
300 return rc;
301 }
302
303 static void
_prog_wait(pid_t pid,int * pstatus)304 _prog_wait (pid_t pid, int *pstatus)
305 {
306 if (pid > 0)
307 {
308 pid_t t;
309 do
310 t = waitpid (pid, pstatus, 0);
311 while (t == -1 && errno == EINTR);
312 }
313 }
314
315 static void
_prog_done(mu_stream_t stream)316 _prog_done (mu_stream_t stream)
317 {
318 int status;
319 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
320
321 mu_argcv_free (fs->argc, fs->argv);
322 free (fs->progname);
323 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
324 free (fs->hints.mu_prog_workdir);
325 if (fs->hint_flags & MU_PROG_HINT_INPUT)
326 mu_stream_unref (fs->hints.mu_prog_input);
327
328 if (fs->in)
329 mu_stream_destroy (&fs->in);
330 if (fs->out)
331 mu_stream_destroy (&fs->out);
332
333 _prog_wait (fs->pid, &fs->status);
334 fs->pid = -1;
335 _prog_wait (fs->writer_pid, &status);
336 fs->writer_pid = -1;
337
338 _prog_stream_unregister (fs);
339 }
340
341 static int
_prog_close(mu_stream_t stream)342 _prog_close (mu_stream_t stream)
343 {
344 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
345 int status;
346
347 if (!stream)
348 return EINVAL;
349
350 if (fs->pid <= 0)
351 return 0;
352
353 mu_stream_close (fs->out);
354 mu_stream_destroy (&fs->out);
355
356 _prog_wait (fs->pid, &fs->status);
357 fs->pid = -1;
358 _prog_wait (fs->writer_pid, &status);
359 fs->writer_pid = -1;
360
361 mu_stream_close (fs->in);
362 mu_stream_destroy (&fs->in);
363
364 if (WIFEXITED (fs->status))
365 {
366 if (WEXITSTATUS (fs->status) == 0)
367 return 0;
368 else if (WEXITSTATUS (fs->status) == 127)
369 return MU_ERR_PROCESS_NOEXEC;
370 else
371 return MU_ERR_PROCESS_EXITED;
372 }
373 else if (WIFSIGNALED (fs->status))
374 return MU_ERR_PROCESS_SIGNALED;
375 return MU_ERR_PROCESS_UNKNOWN_FAILURE;
376 }
377
378 static int
feed_input(struct _mu_prog_stream * fs)379 feed_input (struct _mu_prog_stream *fs)
380 {
381 pid_t pid;
382 int rc = 0;
383
384 pid = fork ();
385 switch (pid)
386 {
387 default:
388 /* Master */
389 fs->writer_pid = pid;
390 mu_stream_close (fs->out);
391 mu_stream_destroy (&fs->out);
392 break;
393
394 case 0:
395 /* Child */
396 mu_stream_copy (fs->out, fs->hints.mu_prog_input, 0, NULL);
397 mu_stream_close (fs->out);
398 exit (0);
399
400 case -1:
401 rc = errno;
402 }
403
404 return rc;
405 }
406
407 static int
_prog_open(mu_stream_t stream)408 _prog_open (mu_stream_t stream)
409 {
410 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
411 int rc;
412 int pfd[2];
413 int flags;
414 int seekable_flag;
415
416 if (!fs || fs->argc == 0)
417 return EINVAL;
418
419 if (fs->pid)
420 {
421 _prog_close (stream);
422 }
423
424 mu_stream_get_flags (stream, &flags);
425 seekable_flag = (flags & MU_STREAM_SEEK);
426
427 rc = start_program_filter (pfd, fs, flags);
428 if (rc)
429 return rc;
430
431 if (REDIRECT_STDOUT_P (flags))
432 {
433 rc = mu_stdio_stream_create (&fs->in, pfd[0],
434 MU_STREAM_READ|seekable_flag);
435 if (rc)
436 {
437 _prog_close (stream);
438 return rc;
439 }
440 }
441
442 if (REDIRECT_STDIN_P (flags))
443 {
444 rc = mu_stdio_stream_create (&fs->out, pfd[1],
445 MU_STREAM_WRITE|seekable_flag);
446 if (rc)
447 {
448 _prog_close (stream);
449 return rc;
450 }
451 }
452
453 _prog_stream_register (fs);
454 if (fs->hint_flags & MU_PROG_HINT_INPUT)
455 return feed_input (fs);
456 return 0;
457 }
458
459 static int
_prog_read(mu_stream_t stream,char * optr,size_t osize,size_t * pnbytes)460 _prog_read (mu_stream_t stream, char *optr, size_t osize, size_t *pnbytes)
461 {
462 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
463 return mu_stream_read (fs->in, optr, osize, pnbytes);
464 }
465
466 static int
_prog_write(mu_stream_t stream,const char * iptr,size_t isize,size_t * pnbytes)467 _prog_write (mu_stream_t stream, const char *iptr, size_t isize,
468 size_t *pnbytes)
469 {
470 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
471 return mu_stream_write (fs->out, iptr, isize, pnbytes);
472 }
473
474 static int
_prog_flush(mu_stream_t stream)475 _prog_flush (mu_stream_t stream)
476 {
477 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
478 mu_stream_flush (fs->in);
479 mu_stream_flush (fs->out);
480 return 0;
481 }
482
483 static int
_prog_ioctl(struct _mu_stream * str,int code,int opcode,void * ptr)484 _prog_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr)
485 {
486 struct _mu_prog_stream *fstr = (struct _mu_prog_stream *) str;
487
488 switch (code)
489 {
490 case MU_IOCTL_TRANSPORT:
491 if (!ptr)
492 return EINVAL;
493 else
494 {
495 mu_transport_t *ptrans = ptr;
496 mu_transport_t t[2];
497
498 switch (opcode)
499 {
500 case MU_IOCTL_OP_GET:
501 mu_stream_ioctl (fstr->in, MU_IOCTL_TRANSPORT,
502 MU_IOCTL_OP_GET, t);
503 ptrans[0] = t[0];
504 mu_stream_ioctl (fstr->out, MU_IOCTL_TRANSPORT,
505 MU_IOCTL_OP_GET, t);
506 ptrans[1] = t[1];
507 break;
508 case MU_IOCTL_OP_SET:
509 return ENOSYS;
510 default:
511 return EINVAL;
512 }
513 }
514 break;
515
516 case MU_IOCTL_PROGSTREAM:
517 if (!ptr)
518 return EINVAL;
519 switch (opcode)
520 {
521 case MU_IOCTL_PROG_STATUS:
522 *(int*)ptr = fstr->status;
523 break;
524
525 case MU_IOCTL_PROG_PID:
526 *(pid_t*)ptr = fstr->pid;
527 break;
528
529 default:
530 return EINVAL;
531 }
532 break;
533
534 default:
535 return ENOSYS;
536 }
537 return 0;
538 }
539
540 /* NOTE: Steals argv */
541 static struct _mu_prog_stream *
_prog_stream_create(const char * progname,size_t argc,char ** argv,int hint_flags,struct mu_prog_hints * hints,int flags)542 _prog_stream_create (const char *progname, size_t argc, char **argv,
543 int hint_flags, struct mu_prog_hints *hints, int flags)
544 {
545 struct _mu_prog_stream *fs;
546
547 fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags);
548 if (!fs)
549 return NULL;
550
551 fs->progname = strdup (progname);
552 if (!fs->progname)
553 {
554 free (fs);
555 return NULL;
556 }
557 fs->argc = argc;
558 fs->argv = argv;
559 fs->stream.read = _prog_read;
560 fs->stream.write = _prog_write;
561 fs->stream.open = _prog_open;
562 fs->stream.close = _prog_close;
563 fs->stream.ctl = _prog_ioctl;
564 fs->stream.flush = _prog_flush;
565 fs->stream.done = _prog_done;
566
567 if (!hints)
568 fs->hint_flags = 0;
569 else
570 {
571 fs->hint_flags = (hint_flags & _MU_PROG_HINT_MASK) |
572 (hint_flags & _MU_PROG_AVAILABLE_LIMITS);
573 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
574 {
575 fs->hints.mu_prog_workdir = strdup (hints->mu_prog_workdir);
576 if (!fs->hints.mu_prog_workdir)
577 {
578 free (fs);
579 return NULL;
580 }
581 }
582 memcpy (fs->hints.mu_prog_limit, hints->mu_prog_limit,
583 sizeof (fs->hints.mu_prog_limit));
584 fs->hints.mu_prog_prio = hints->mu_prog_prio;
585 if (fs->hint_flags & MU_PROG_HINT_INPUT)
586 {
587 fs->hints.mu_prog_input = hints->mu_prog_input;
588 mu_stream_ref (fs->hints.mu_prog_input);
589 }
590 if (fs->hint_flags & MU_PROG_HINT_UID)
591 {
592 fs->hints.mu_prog_uid = hints->mu_prog_uid;
593 if (fs->hint_flags & MU_PROG_HINT_GID)
594 {
595 fs->hints.mu_prog_gidv = calloc (hints->mu_prog_gidc,
596 sizeof (fs->hints.mu_prog_gidv[0]));
597 if (!fs->hints.mu_prog_gidv)
598 {
599 mu_stream_unref ((mu_stream_t) fs);
600 return NULL;
601 }
602 memcpy (fs->hints.mu_prog_gidv, hints->mu_prog_gidv,
603 hints->mu_prog_gidc * fs->hints.mu_prog_gidv[0]);
604 fs->hints.mu_prog_gidc = hints->mu_prog_gidc;
605 }
606 else
607 {
608 fs->hints.mu_prog_gidc = 0;
609 fs->hints.mu_prog_gidv = NULL;
610 }
611 }
612 }
613
614 return fs;
615 }
616
617 int
mu_prog_stream_create(mu_stream_t * pstream,const char * progname,size_t argc,char ** argv,int hint_flags,struct mu_prog_hints * hints,int flags)618 mu_prog_stream_create (mu_stream_t *pstream,
619 const char *progname, size_t argc, char **argv,
620 int hint_flags,
621 struct mu_prog_hints *hints,
622 int flags)
623 {
624 int rc;
625 mu_stream_t stream;
626 char **xargv;
627 size_t i;
628
629 if (pstream == NULL)
630 return MU_ERR_OUT_PTR_NULL;
631
632 if (progname == NULL)
633 return EINVAL;
634
635 xargv = calloc (argc + 1, sizeof (xargv[0]));
636 if (!xargv)
637 return ENOMEM;
638
639 for (i = 0; i < argc; i++)
640 {
641 xargv[i] = strdup (argv[i]);
642 if (!xargv[i])
643 {
644 mu_argcv_free (i, argv);
645 return ENOMEM;
646 }
647 }
648 stream = (mu_stream_t) _prog_stream_create (progname, argc, xargv,
649 hint_flags, hints, flags);
650 if (!stream)
651 {
652 mu_argcv_free (argc, xargv);
653 return ENOMEM;
654 }
655
656 rc = mu_stream_open (stream);
657 if (rc)
658 mu_stream_destroy (&stream);
659 else
660 *pstream = stream;
661 return rc;
662 }
663
664 int
mu_command_stream_create(mu_stream_t * pstream,const char * command,int flags)665 mu_command_stream_create (mu_stream_t *pstream, const char *command,
666 int flags)
667 {
668 int rc;
669 mu_stream_t stream;
670 struct mu_wordsplit ws;
671
672 if (pstream == NULL)
673 return MU_ERR_OUT_PTR_NULL;
674
675 if (command == NULL)
676 return EINVAL;
677
678 ws.ws_comment = "#";
679 if (mu_wordsplit (command, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
680 {
681 mu_error (_("cannot split line `%s': %s"), command,
682 mu_wordsplit_strerror (&ws));
683 return errno;
684 }
685
686 rc = mu_prog_stream_create (&stream,
687 ws.ws_wordv[0],
688 ws.ws_wordc, ws.ws_wordv,
689 0, NULL, flags);
690 if (rc == 0)
691 {
692 ws.ws_wordc = 0;
693 ws.ws_wordv = NULL;
694 *pstream = stream;
695 }
696 mu_wordsplit_free (&ws);
697
698 return rc;
699 }
700