1/* xqtsub.c
2   System dependent functions used only by uuxqt.
3
4   Copyright (C) 1991, 1992, 1993, 1995, 2002 Ian Lance Taylor
5
6   This file is part of the Taylor UUCP package.
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21
22   The author of the program may be contacted at ian@airs.com.
23   */
24
25#include "uucp.h"
26
27#if USE_RCS_ID
28const char xqtsub_rcsid[] = "$Id: xqtsub.c,v 1.24 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#include "uudefs.h"
32#include "uuconf.h"
33#include "system.h"
34#include "sysdep.h"
35
36#include <ctype.h>
37#include <errno.h>
38
39#if HAVE_FCNTL_H
40#include <fcntl.h>
41#else
42#if HAVE_SYS_FILE_H
43#include <sys/file.h>
44#endif
45#endif
46
47#ifndef O_RDONLY
48#define O_RDONLY 0
49#define O_WRONLY 1
50#define O_RDWR 2
51#endif
52
53#ifndef O_NOCTTY
54#define O_NOCTTY 0
55#endif
56
57#ifndef FD_CLOEXEC
58#define FD_CLOEXEC 1
59#endif
60
61#if HAVE_OPENDIR
62#if HAVE_DIRENT_H
63#include <dirent.h>
64#else /* ! HAVE_DIRENT_H */
65#include <sys/dir.h>
66#define dirent direct
67#endif /* ! HAVE_DIRENT_H */
68#endif /* HAVE_OPENDIR */
69
70/* Get a value for EX_TEMPFAIL.  */
71
72#if HAVE_SYSEXITS_H
73#include <sysexits.h>
74#endif
75
76#ifndef EX_TEMPFAIL
77#define EX_TEMPFAIL 75
78#endif
79
80static boolean fclean_uuxqt_dir P((const char *zxqtdir));
81
82/* Get the full pathname of the command to execute, given the list of
83   permitted commands and the allowed path.  */
84
85char *
86zsysdep_find_command (zcmd, pzcmds, pzpath, pferr)
87     const char *zcmd;
88     char **pzcmds;
89     char **pzpath;
90     boolean *pferr;
91{
92  char **pz;
93  struct stat s;
94
95  *pferr = FALSE;
96
97  for (pz = pzcmds; *pz != NULL; pz++)
98    {
99      char *zslash;
100
101      if (strcmp (*pz, "ALL") == 0)
102	break;
103
104      zslash = strrchr (*pz, '/');
105      if (zslash != NULL)
106	++zslash;
107      else
108	zslash = *pz;
109      if (strcmp (zslash, zcmd) == 0
110	  || strcmp (*pz, zcmd) == 0)
111	{
112	  /* If we already have an absolute path, we can get out
113	     immediately.  */
114	  if (**pz == '/')
115	    {
116	      /* Quick error check.  */
117	      if (stat (*pz, &s) != 0)
118		{
119		  ulog (LOG_ERROR, "%s: %s", *pz, strerror (errno));
120		  *pferr = TRUE;
121		  return NULL;
122		}
123	      return zbufcpy (*pz);
124	    }
125	  break;
126	}
127    }
128
129  /* If we didn't find this command, get out.  */
130  if (*pz == NULL)
131    return NULL;
132
133  /* We didn't find an absolute pathname, so we must look through
134     the path.  */
135  for (pz = pzpath; *pz != NULL; pz++)
136    {
137      char *zname;
138
139      zname = zsysdep_in_dir (*pz, zcmd);
140      if (stat (zname, &s) == 0)
141	return zname;
142    }
143
144  return NULL;
145}
146
147/* Expand a local filename for uuxqt.  This is special because uuxqt
148   only wants to expand filenames that start with ~ (it does not want
149   to prepend the current directory to other names) and if the ~ is
150   double, it is turned into a single ~.  This returns NULL to
151   indicate that no change was required; it has no way to return
152   error.  */
153
154char *
155zsysdep_xqt_local_file (qsys, zfile)
156     const struct uuconf_system *qsys;
157     const char *zfile;
158{
159  if (*zfile != '~')
160    return NULL;
161  if (zfile[1] == '~')
162    {
163      size_t clen;
164      char *zret;
165
166      clen = strlen (zfile);
167      zret = zbufalc (clen);
168      memcpy (zret, zfile + 1, clen);
169      return zret;
170    }
171  return zsysdep_local_file (zfile, qsys->uuconf_zpubdir,
172			     (boolean *) NULL);
173}
174
175#if ! ALLOW_FILENAME_ARGUMENTS
176
177/* Check to see whether an argument specifies a file name; if it does,
178   make sure that the file may legally be sent and/or received.  For
179   Unix, we do not permit any occurrence of "/../" in the name, nor
180   may it start with "../".  Otherwise, if it starts with "/" we check
181   against the list of permitted files.  */
182
183boolean
184fsysdep_xqt_check_file (qsys, zfile)
185     const struct uuconf_system *qsys;
186     const char *zfile;
187{
188  size_t clen;
189
190  /* Disallow exact "..", prefix "../", suffix "/..", internal "/../",
191     and restricted absolute paths.  */
192  clen = strlen (zfile);
193  if ((clen == sizeof ".." - 1
194       && strcmp (zfile, "..") == 0)
195      || strncmp (zfile, "../", sizeof "../" - 1) == 0
196      || (clen >= sizeof "/.." - 1
197	  && strcmp (zfile + clen - (sizeof "/.." - 1), "/..") == 0)
198      || strstr (zfile, "/../") != NULL
199      || (*zfile == '/'
200	  && (! fin_directory_list (zfile, qsys->uuconf_pzremote_send,
201				    qsys->uuconf_zpubdir, TRUE, FALSE,
202				    (const char *) NULL)
203	      || ! fin_directory_list (zfile, qsys->uuconf_pzremote_receive,
204				       qsys->uuconf_zpubdir, TRUE, FALSE,
205				       (const char *) NULL))))
206    {
207      ulog (LOG_ERROR, "Not permitted to refer to file \"%s\"", zfile);
208      return FALSE;
209    }
210
211  return TRUE;
212}
213
214#endif /* ! ALLOW_FILENAME_ARGUMENTS */
215
216/* Invoke the command specified by an execute file.  */
217
218/*ARGSUSED*/
219boolean
220fsysdep_execute (qsys, zuser, pazargs, zfullcmd, zinput, zoutput,
221		 zchdir, fshell, iseq, pzerror, pftemp)
222     const struct uuconf_system *qsys;
223     const char *zuser;
224     const char **pazargs;
225     const char *zfullcmd ATTRIBUTE_UNUSED;
226     const char *zinput;
227     const char *zoutput;
228     const char *zchdir;
229     boolean fshell;
230     int iseq;
231     char **pzerror;
232     boolean *pftemp;
233{
234  int aidescs[3];
235  boolean ferr;
236  pid_t ipid;
237  int ierr;
238  char abxqtdir[sizeof XQTDIR + 4];
239  const char *zxqtdir;
240  int istat;
241  char *zpath;
242#if ALLOW_SH_EXECUTION
243  const char *azshargs[4];
244#endif
245
246  *pzerror = NULL;
247  *pftemp = FALSE;
248
249  aidescs[0] = SPAWN_NULL;
250  aidescs[1] = SPAWN_NULL;
251  aidescs[2] = SPAWN_NULL;
252
253  ferr = FALSE;
254
255  if (zinput != NULL)
256    {
257      aidescs[0] = open ((char *) zinput, O_RDONLY | O_NOCTTY, 0);
258      if (aidescs[0] < 0)
259	{
260	  ulog (LOG_ERROR, "open (%s): %s", zinput, strerror (errno));
261	  ferr = TRUE;
262	}
263      else if (fcntl (aidescs[0], F_SETFD,
264		      fcntl (aidescs[0], F_GETFD, 0) | FD_CLOEXEC) < 0)
265	{
266	  ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
267	  ferr = TRUE;
268	}
269    }
270
271  if (! ferr && zoutput != NULL)
272    {
273      aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE);
274      if (aidescs[1] < 0)
275	{
276	  if (errno == ENOENT && zoutput[0] != '/')
277	    {
278	      if (! fsysdep_make_dirs (zoutput, FALSE))
279		{
280		  *pftemp = TRUE;
281		  ferr = TRUE;
282		}
283	      else
284		aidescs[1] = creat ((char *) zoutput, IPRIVATE_FILE_MODE);
285	    }
286	  if (! ferr && aidescs[1] < 0)
287	    {
288	      ulog (LOG_ERROR, "creat during fsysdep_execute part A (%s) in %s: %s", zoutput, getwd(NULL),
289	        strerror (errno));
290	      *pftemp = TRUE;
291	      ferr = TRUE;
292	    }
293	}
294      if (! ferr
295	  && fcntl (aidescs[1], F_SETFD,
296		    fcntl (aidescs[1], F_GETFD, 0) | FD_CLOEXEC) < 0)
297	{
298	  ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
299	  ferr = TRUE;
300	}
301    }
302
303  if (! ferr)
304    {
305      *pzerror = zstemp_file (qsys);
306      aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE);
307      if (aidescs[2] < 0)
308	{
309	  if (errno == ENOENT)
310	    {
311	      if (! fsysdep_make_dirs (*pzerror, FALSE))
312		{
313		  *pftemp = TRUE;
314		  ferr = TRUE;
315		}
316	      else
317		aidescs[2] = creat (*pzerror, IPRIVATE_FILE_MODE);
318	    }
319	  if (! ferr && aidescs[2] < 0)
320	    {
321	      ulog (LOG_ERROR, "creat during fsysdep_execute part B (%s): %s", *pzerror, strerror (errno));
322	      *pftemp = TRUE;
323	      ferr = TRUE;
324	    }
325	}
326      if (! ferr
327	  && fcntl (aidescs[2], F_SETFD,
328		    fcntl (aidescs[2], F_GETFD, 0) | FD_CLOEXEC) < 0)
329	{
330	  ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
331	  ferr = TRUE;
332	}
333    }
334
335  if (iseq == 0)
336    zxqtdir = XQTDIR;
337  else
338    {
339      sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
340      zxqtdir = abxqtdir;
341    }
342
343  if (ferr)
344    {
345      if (aidescs[0] != SPAWN_NULL)
346	(void) close (aidescs[0]);
347      if (aidescs[1] != SPAWN_NULL)
348	(void) close (aidescs[1]);
349      if (aidescs[2] != SPAWN_NULL)
350	(void) close (aidescs[2]);
351      ubuffree (*pzerror);
352      return FALSE;
353    }
354
355#if ALLOW_SH_EXECUTION
356  if (fshell)
357    {
358      azshargs[0] = "/bin/sh";
359      azshargs[1] = "-c";
360      azshargs[2] = zfullcmd;
361      azshargs[3] = NULL;
362      pazargs = azshargs;
363    }
364#else
365  fshell = FALSE;
366#endif
367
368  if (qsys->uuconf_pzpath == NULL)
369    zpath = NULL;
370  else
371    {
372      size_t c;
373      char **pz;
374
375      c = 0;
376      for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++)
377	c += strlen (*pz) + 1;
378      zpath = zbufalc (c);
379      *zpath = '\0';
380      for (pz = qsys->uuconf_pzpath; *pz != NULL; pz++)
381	{
382	  strcat (zpath, *pz);
383	  if (pz[1] != NULL)
384	    strcat (zpath, ":");
385	}
386    }
387
388  /* Pass zchdir as zxqtdir, fnosigs as TRUE, fshell as TRUE if we
389     aren't already using the shell.  */
390  ipid = ixsspawn (pazargs, aidescs, TRUE, FALSE, zchdir ? zchdir : zxqtdir,
391    TRUE, !fshell, zpath, qsys->uuconf_zname, zuser);
392
393  ierr = errno;
394
395  ubuffree (zpath);
396
397  if (aidescs[0] != SPAWN_NULL)
398    (void) close (aidescs[0]);
399  if (aidescs[1] != SPAWN_NULL)
400    (void) close (aidescs[1]);
401  if (aidescs[2] != SPAWN_NULL)
402    (void) close (aidescs[2]);
403
404  if (ipid < 0)
405    {
406      ulog (LOG_ERROR, "ixsspawn: %s", strerror (ierr));
407      *pftemp = TRUE;
408      return FALSE;
409    }
410
411  istat = ixswait ((unsigned long) ipid, "Execution");
412
413  if (istat == EX_TEMPFAIL)
414    *pftemp = TRUE;
415
416  return istat == 0;
417}
418
419/* Lock a uuxqt process.  */
420
421int
422ixsysdep_lock_uuxqt (zcmd, cmaxuuxqts)
423     const char *zcmd;
424     int cmaxuuxqts;
425{
426  char ab[sizeof "LCK.XQT.9999"];
427  int i;
428
429  if (cmaxuuxqts <= 0 || cmaxuuxqts >= 10000)
430    cmaxuuxqts = 9999;
431  for (i = 0; i < cmaxuuxqts; i++)
432    {
433      sprintf (ab, "LCK.XQT.%d", i);
434      if (fsdo_lock (ab, TRUE, (boolean *) NULL))
435	break;
436    }
437  if (i >= cmaxuuxqts)
438    return -1;
439
440  if (zcmd != NULL)
441    {
442      char abcmd[sizeof "LXQ.123456789"];
443
444      sprintf (abcmd, "LXQ.%.9s", zcmd);
445      abcmd[strcspn (abcmd, " \t/")] = '\0';
446      if (! fsdo_lock (abcmd, TRUE, (boolean *) NULL))
447	{
448	  (void) fsdo_unlock (ab, TRUE);
449	  return -1;
450	}
451    }
452
453  return i;
454}
455
456/* Unlock a uuxqt process.  */
457
458boolean
459fsysdep_unlock_uuxqt (iseq, zcmd, cmaxuuxqts)
460     int iseq;
461     const char *zcmd;
462     int cmaxuuxqts ATTRIBUTE_UNUSED;
463{
464  char ab[sizeof "LCK.XQT.9999"];
465  boolean fret;
466
467  fret = TRUE;
468
469  sprintf (ab, "LCK.XQT.%d", iseq);
470  if (! fsdo_unlock (ab, TRUE))
471    fret = FALSE;
472
473  if (zcmd != NULL)
474    {
475      char abcmd[sizeof "LXQ.123456789"];
476
477      sprintf (abcmd, "LXQ.%.9s", zcmd);
478      abcmd[strcspn (abcmd, " \t/")] = '\0';
479      if (! fsdo_unlock (abcmd, TRUE))
480	fret = FALSE;
481    }
482
483  return fret;
484}
485
486/* See whether a particular uuxqt command is locked (this depends on
487   the implementation of fsdo_lock).  */
488
489boolean
490fsysdep_uuxqt_locked (zcmd)
491     const char *zcmd;
492{
493  char ab[sizeof "LXQ.123456789"];
494  struct stat s;
495
496  sprintf (ab, "LXQ.%.9s", zcmd);
497  return stat (ab, &s) == 0;
498}
499
500/* Lock a particular execute file.  */
501
502boolean
503fsysdep_lock_uuxqt_file (zfile)
504     const char *zfile;
505{
506  char *zcopy, *z;
507  boolean fret;
508
509  zcopy = zbufcpy (zfile);
510
511  z = strrchr (zcopy, '/');
512  if (z == NULL)
513    *zcopy = 'L';
514  else
515    *(z + 1) = 'L';
516
517  fret = fsdo_lock (zcopy, TRUE, (boolean *) NULL);
518  ubuffree (zcopy);
519  return fret;
520}
521
522/* Unlock a particular execute file.  */
523
524boolean
525fsysdep_unlock_uuxqt_file (zfile)
526     const char *zfile;
527{
528  char *zcopy, *z;
529  boolean fret;
530
531  zcopy = zbufcpy (zfile);
532
533  z = strrchr (zcopy, '/');
534  if (z == NULL)
535    *zcopy = 'L';
536  else
537    *(z + 1) = 'L';
538
539  fret = fsdo_unlock (zcopy, TRUE);
540  ubuffree (zcopy);
541  return fret;
542}
543
544/* Lock the execute directory.  Since we use a different directory
545   depending on which LCK.XQT.dddd file we got, there is actually no
546   need to create a lock file.  We do make sure that the directory
547   exists, though, and that it is empty.  */
548
549boolean
550fsysdep_lock_uuxqt_dir (iseq)
551     int iseq;
552{
553  const char *zxqtdir;
554  char abxqtdir[sizeof XQTDIR + 4];
555
556  if (iseq == 0)
557    zxqtdir = XQTDIR;
558  else
559    {
560      sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
561      zxqtdir = abxqtdir;
562    }
563
564  if (mkdir (zxqtdir, S_IRWXU) < 0
565      && errno != EEXIST
566      && errno != EISDIR)
567    {
568      ulog (LOG_ERROR, "mkdir (%s): %s", zxqtdir, strerror (errno));
569      return FALSE;
570    }
571
572  return fclean_uuxqt_dir (zxqtdir);
573}
574
575/* Unlock the execute directory and clear it out.  The lock is
576   actually the LCK.XQT.dddd file, so we don't unlock it, but we do
577   remove all the files.  */
578
579boolean
580fsysdep_unlock_uuxqt_dir (iseq)
581     int iseq;
582{
583  const char *zxqtdir;
584  char abxqtdir[sizeof XQTDIR + 4];
585
586  if (iseq == 0)
587    zxqtdir = XQTDIR;
588  else
589    {
590      sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
591      zxqtdir = abxqtdir;
592    }
593
594  return fclean_uuxqt_dir (zxqtdir);
595}
596
597static boolean
598fclean_uuxqt_dir (zxqtdir)
599     const char *zxqtdir;
600{
601  DIR *qdir;
602
603  qdir = opendir ((char *) zxqtdir);
604  if (qdir != NULL)
605    {
606      struct dirent *qentry;
607
608      while ((qentry = readdir (qdir)) != NULL)
609	{
610	  char *z;
611
612	  if (strcmp (qentry->d_name, ".") == 0
613	      || strcmp (qentry->d_name, "..") == 0)
614	    continue;
615	  z = zsysdep_in_dir (zxqtdir, qentry->d_name);
616	  if (remove (z) < 0)
617	    {
618	      int ierr;
619
620	      ierr = errno;
621	      if (! fsysdep_directory (z))
622		ulog (LOG_ERROR, "remove (%s): %s", z,
623		      strerror (ierr));
624	      else
625		(void) fsysdep_rmdir (z);
626	    }
627	  ubuffree (z);
628	}
629
630      closedir (qdir);
631    }
632
633  return TRUE;
634}
635
636/* Move files into the execution directory.  */
637
638boolean
639fsysdep_copy_uuxqt_files (cfiles, pzfrom, pzto, iseq, pzinput)
640     int cfiles;
641     const char *const *pzfrom;
642     const char *const *pzto;
643     int iseq;
644     char **pzinput;
645{
646  char *zinput;
647  const char *zxqtdir;
648  char abxqtdir[sizeof XQTDIR + 4];
649  int i;
650
651  if (pzinput == NULL)
652    zinput = NULL;
653  else
654    zinput = *pzinput;
655
656  if (iseq == 0)
657    zxqtdir = XQTDIR;
658  else
659    {
660      sprintf (abxqtdir, "%s%04d", XQTDIR, iseq);
661      zxqtdir = abxqtdir;
662    }
663
664  for (i = 0; i < cfiles; i++)
665    {
666      const char *zfrom, *zto;
667      char *zfree;
668
669      if (pzto[i] == NULL)
670	continue;
671
672      zfree = zsysdep_in_dir (zxqtdir, pzto[i]);
673
674      zfrom = pzfrom[i];
675      zto = zfree;
676
677      if (zinput != NULL && strcmp (zinput, zfrom) == 0)
678	{
679	  *pzinput = zbufcpy (zto);
680	  zinput = NULL;
681	}
682
683      if (link (zfrom, zto) < 0)
684	{
685	  if (errno != EXDEV && errno != EEXIST && errno != EMLINK)
686	    {
687	      ulog (LOG_ERROR, "link (%s, %s): %s", zfrom, zto,
688		    strerror (errno));
689	      ubuffree (zfree);
690	      return FALSE;
691	    }
692
693	  if (! fcopy_file (zfrom, zto, FALSE, FALSE, FALSE))
694	    {
695	      ubuffree (zfree);
696	      return FALSE;
697	    }
698	}
699
700      (void) chmod (zto, IPUBLIC_FILE_MODE);
701#ifdef WORLD_WRITABLE_FILE_IN
702  char rfile[PATH_MAX];
703  realpath(zto, rfile);
704  ulog(LOG_ERROR, "open of %s WORLD_WRITABLE_FILE_IN is %s", rfile, WORLD_WRITABLE_FILE_IN);
705  if (rfile == strstr(rfile, WORLD_WRITABLE_FILE_IN)) {
706      chmod(zto, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
707      ulog(LOG_ERROR, "xqtsub chmod'ing %s", zto);
708  }
709#endif
710
711      ubuffree (zfree);
712    }
713
714  return TRUE;
715}
716