Merge branch 'master' of chiark:src/checkpath
authorMark Wooding <mdw@distorted.org.uk>
Sun, 13 Apr 2008 17:23:33 +0000 (18:23 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 13 Apr 2008 17:23:33 +0000 (18:23 +0100)
* 'master' of chiark:src/checkpath:
  tmpdir: Allow trusting of particular groups.
  tmpdir: Introduce option for verbose reporting, for diagnosing problems.
  Improve formatting before we get too stuck in.

Conflicts:

tmpdir.c

1  2 
checkpath.c
checkpath.h
chkpath.c
tmpdir.1
tmpdir.c

diff --combined checkpath.c
@@@ -1,13 -1,11 +1,11 @@@
  /* -*-c-*-
   *
-  * $Id: checkpath.c,v 1.6 2004/04/08 01:36:22 mdw Exp $
-  *
   * Check a path for safety
   *
   * (c) 1999 Mark Wooding
   */
  
 -/*----- Licensing notice --------------------------------------------------* 
 +/*----- Licensing notice --------------------------------------------------*
   *
   * This file is part of chkpath.
   *
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
 - * 
 + *
   * chkpath is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
 - * 
 + *
   * You should have received a copy of the GNU General Public License
   * along with chkpath; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@@ -28,8 -26,6 +26,8 @@@
  
  /*----- Header files ------------------------------------------------------*/
  
 +#include "config.h"
 +
  #include <errno.h>
  #include <stdarg.h>
  #include <stdio.h>
@@@ -154,10 -150,7 +152,7 @@@ static void pop(void
   */
  
  static void popall(void)
- {
-   while (sp->e_link)
-     pop();
- }
+   { while (sp->e_link) pop(); }
  
  /* --- @push@ --- *
   *
@@@ -193,75 -186,82 +188,82 @@@ static void push(struct elt *e
  static void report(const struct checkpath *cp, unsigned what, int verbose,
                   const char *p, const char *msg, ...)
  {
+   dstr d = DSTR_INIT;
+   va_list ap;
+   const char *q = msg;
+   const char *s;
+   size_t n;
+   int e = errno;
+   uid_t u;
+   struct passwd *pw;
+   gid_t g;
+   struct group *gr;
    /* --- Decide whether to bin this message --- */
  
    if (!cp->cp_report || verbose > cp->cp_verbose || !(cp->cp_what & what))
      return;
  
+   /* --- If no reporting, do the easy thing --- */
+   if (!(cp->cp_what & CP_REPORT)) {
+     cp->cp_report(what, verbose, p, 0, cp->cp_arg);
+     return;
+   }
    /* --- Format the message nicely --- */
  
-   if (cp->cp_what & CP_REPORT) {
-     dstr d = DSTR_INIT;
-     va_list ap;
-     const char *q = msg;
-     size_t n;
-     int e = errno;
-     va_start(ap, msg);
-     if (verbose > 1)
-       dstr_puts(&d, "[ ");
-     if (p)
-       dstr_putf(&d, "Path: %s: ", p);
-     while (*q) {
-       if (*q == '%') {
-       q++;
-       switch (*q) {
-         case 'e':
-           dstr_puts(&d, strerror(e));
-           break;
-         case 'u': {
-           uid_t u = (uid_t)va_arg(ap, int);
-           struct passwd *pw = getpwuid(u);
-           if (pw)
-             dstr_putf(&d, "`%s'", pw->pw_name);
-           else
-             dstr_putf(&d, "%i", (int)u);
-         } break;
-         case 'g': {
-           gid_t g = (gid_t)va_arg(ap, int);
-           struct group *gr = getgrgid(g);
-           if (gr)
-             dstr_putf(&d, "`%s'", gr->gr_name);
-           else
-             dstr_putf(&d, "%i", (int)g);
-         } break;
-         case 's': {
-           const char *s = va_arg(ap, const char *);
-           dstr_puts(&d, s);
-         } break;
-         case '%':
-           dstr_putc(&d, '%');
-           break;
-         default:
-           dstr_putc(&d, '%');
-           dstr_putc(&d, *q);
-           break;
-       }
-       q++;
-       } else {
-       n = strcspn(q, "%");
-       DPUTM(&d, q, n);
-       q += n;
+   va_start(ap, msg);
+   if (verbose > 1)
+     dstr_puts(&d, "[ ");
+   if (p)
+     dstr_putf(&d, "Path: %s: ", p);
+   while (*q) {
+     if (*q == '%') {
+       q++;
+       switch (*q) {
+       case 'e':
+         dstr_puts(&d, strerror(e));
+         break;
+       case 'u':
+         u = (uid_t)va_arg(ap, int);
+         if ((pw = getpwuid(u)) != 0)
+           dstr_putf(&d, "`%s'", pw->pw_name);
+         else
+           dstr_putf(&d, "%i", (int)u);
+         break;
+       case 'g':
+         g = (gid_t)va_arg(ap, int);
+         if ((gr = getgrgid(g)) != 0)
+           dstr_putf(&d, "`%s'", gr->gr_name);
+         else
+           dstr_putf(&d, "%i", (int)g);
+         break;
+       case 's':
+         s = va_arg(ap, const char *);
+         dstr_puts(&d, s);
+         break;
+       case '%':
+         dstr_putc(&d, '%');
+         break;
+       default:
+         dstr_putc(&d, '%');
+         dstr_putc(&d, *q);
+         break;
        }
+       q++;
+     } else {
+       n = strcspn(q, "%");
+       DPUTM(&d, q, n);
+       q += n;
      }
-     if (verbose > 1)
-       dstr_puts(&d, " ]");
-     DPUTZ(&d);
-     cp->cp_report(what, verbose, p, d.buf, cp->cp_arg);
-     dstr_destroy(&d);
-     va_end(ap);
-   } else
-     cp->cp_report(what, verbose, p, 0, cp->cp_arg);
+   }
+   if (verbose > 1)
+     dstr_puts(&d, " ]");
+   DPUTZ(&d);
+   cp->cp_report(what, verbose, p, d.buf, cp->cp_arg);
+   dstr_destroy(&d);
+   va_end(ap);
  }
  
  /* --- @sanity@ --- *
@@@ -281,6 -281,8 +283,8 @@@ static unsigned sanity(const char *p, s
  {
    unsigned bad = 0;
    int stickyok = 0;
+   int i;
+   unsigned b;
  
    if (S_ISDIR(st->st_mode) &&
        (!(f & f_last) || (cp->cp_what & CP_STICKYOK)))
  
    if ((cp->cp_what & (CP_WRGRP | CP_WROTHGRP)) &&
        (st->st_mode & (0020 | stickyok)) == 0020) {
-     int i;
-     unsigned b = CP_WRGRP;
+     b = CP_WRGRP;
  
      if (cp->cp_what & CP_WROTHGRP) {
        b = CP_WROTHGRP;
@@@ -501,7 -502,7 +504,7 @@@ void checkpath_setids(struct checkpath 
  
    cp->cp_uid = getuid();
    n = getgroups(sizeof(cp->cp_gid) / sizeof(cp->cp_gid[0]), cp->cp_gid);
 -  
 +
    for (i = 0; i < n; i++) {
      if (cp->cp_gid[i] == g)
        goto gid_ok;
diff --combined checkpath.h
@@@ -1,13 -1,11 +1,11 @@@
  /* -*-c-*-
   *
-  * $Id: checkpath.h,v 1.4 2004/04/08 01:36:22 mdw Exp $
-  *
   * Check a path for safety
   *
   * (c) 1999 Mark Wooding
   */
  
 -/*----- Licensing notice --------------------------------------------------* 
 +/*----- Licensing notice --------------------------------------------------*
   *
   * This file is part of chkpath.
   *
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
 - * 
 + *
   * chkpath is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
 - * 
 + *
   * You should have received a copy of the GNU General Public License
   * along with chkpath; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
diff --combined chkpath.c
+++ b/chkpath.c
@@@ -1,13 -1,11 +1,11 @@@
  /* -*-c-*-
   *
-  * $Id: chkpath.c,v 1.4 2004/04/08 01:36:22 mdw Exp $
-  *
   * Check a user's file search path
   *
   * (c) 1999 Mark Wooding
   */
  
 -/*----- Licensing notice --------------------------------------------------* 
 +/*----- Licensing notice --------------------------------------------------*
   *
   * This file is part of chkpath.
   *
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
 - * 
 + *
   * chkpath is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
 - * 
 + *
   * You should have received a copy of the GNU General Public License
   * along with chkpath; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
@@@ -28,8 -26,6 +26,8 @@@
  
  /*----- Header files ------------------------------------------------------*/
  
 +#include "config.h"
 +
  #include <errno.h>
  #include <limits.h>
  #include <stdio.h>
@@@ -45,6 -41,8 +43,8 @@@
  
  /*----- Main code ---------------------------------------------------------*/
  
+ /* --- @report@ --- */
  static void report(unsigned what, int verbose,
                   const char *p, const char *msg,
                   void *arg)
@@@ -126,7 -124,7 +126,7 @@@ int main(int argc, char *argv[]
        { "print",      0,              0,      'p' },
        { 0,            0,              0,      0 }
      };
 -    int i = mdwopt(argc, argv, "hVu vqstp", opts, 0, 0, 0);
 +    int i = mdwopt(argc, argv, "hVu" "vqstp", opts, 0, 0, 0);
  
      if (i < 0)
        break;
diff --combined tmpdir.1
+++ b/tmpdir.1
@@@ -4,14 -4,16 +4,16 @@@
  tmpdir \- choose, or check a choice of, temporary directory
  .SH SYNOPSIS
  .B tmpdir
- .RB [ \-bc ]
- .RB [ \-v
+ .RB [ \-bcv ]
+ .RB [ \-g
+ .IR group ]
+ .RB [ \-C
  .IR dir ]
  .SH USAGE
  The
  .B tmpdir
  program creates a secure place for temporary files to be stored, and
 -outputs an assignment to the 
 +outputs an assignment to the
  .B TMPDIR
  variable suitable for execution by a shell.
  .PP
@@@ -71,7 -73,15 +73,15 @@@ examine the user's shell and decide whi
  .B "\-c, \-\-cshell"
  Output an assignment using C shell syntax.
  .TP
- .BI "\-v, --verify " dir
+ .BI "\-g, \-\-group " group
+ Trust (the members of)
+ .IR group :
+ consider directories they can write to be safe.
+ .TP
+ .B "-v, \-\-verbose"
+ Report problems to standard error.  Repeat for more verbosity.
+ .TP
+ .BI "\-C, --check " dir
  Don't try to find a temporary directory; just see whether
  .I dir
  is secure, and exit successfully if it is (and unsuccessfully if it
diff --combined tmpdir.c
+++ b/tmpdir.c
@@@ -1,13 -1,11 +1,11 @@@
  /* -*-c-*-
   *
-  * $Id: tmpdir.c,v 1.5 2004/04/08 01:36:22 mdw Exp $
-  *
   * Choose and check temporary directories
   *
   * (c) 1999 Mark Wooding
   */
  
 -/*----- Licensing notice --------------------------------------------------* 
 +/*----- Licensing notice --------------------------------------------------*
   *
   * This file is part of chkpath.
   *
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
 - * 
 + *
   * chkpath is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
 - * 
 + *
   * You should have received a copy of the GNU General Public License
   * along with chkpath; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  
  /*----- Header files ------------------------------------------------------*/
  
 +#include "config.h"
 +
  #include <errno.h>
+ #include <ctype.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/stat.h>
  #include <unistd.h>
  #include <pwd.h>
+ #include <grp.h>
  
  #include <mLib/alloc.h>
  #include <mLib/dstr.h>
+ #include <mLib/macros.h>
  #include <mLib/mdwopt.h>
  #include <mLib/quis.h>
  #include <mLib/report.h>
@@@ -182,7 -181,9 +183,7 @@@ good
   */
  
  static int fullcheck(const char *p)
 -{
 -  return (checkpath(p, &cp) == 0 && ok(p, 0));
 -}
 +  { return (checkpath(p, &cp) == 0 && ok(p, 0)); }
  
  /* --- @goodtmp@ --- *
   *
@@@ -205,12 -206,16 +206,12 @@@ static char *goodtmp(void
  
    /* --- Try making a directory in `/tmp' --- */
  
 -  if (!(q = getenv("USER")) && !(q = getenv("LOGNAME")))
 -    q = pw->pw_name;
 -  if ((q = trytmp("/tmp", q)) != 0)
 +  if ((q = trytmp("/tmp", pw->pw_name)) != 0)
      return (q);
  
    /* --- That failed: try a directory in the user's home --- */
  
 -  if (!(q = getenv("HOME")))
 -    q = pw->pw_dir;
 -  if ((q = trytmp(q, "tmp")) != 0)
 +  if ((q = trytmp(pw->pw_dir, "tmp")) != 0)
      return (q);
  
    /* --- Still no joy: give up --- *
    return (0);
  }
  
+ /* --- @report@ --- */
+ static void report(unsigned what, int verbose,
+                  const char *p, const char *msg,
+                  void *arg)
+   { moan("%s", msg); }
  /* --- @usage@ --- */
  
  static void usage(FILE *fp)
@@@ -254,7 -266,9 +262,9 @@@ Options supported:\n
  \n\
  -b, --bourne          Output a `TMPDIR' setting for Bourne shell users.\n\
  -c, --cshell          Output a `TMPDIR' setting for C shell users.\n\
- -v, --verify PATH     Check whether PATH is good, setting exit status.\n\
+ -v, --verbose         Report problems to standard error.\n\
+ -g, --group NAME      Trust group NAME to be honest and true.\n\
+ -C, --check PATH      Check whether PATH is good, setting exit status.\n\
  \n\
  The default action is to examine the caller's shell and output a suitable\n\
  setting for that shell type.\n\
        fp);
  }
  
+ /* --- @allowgroup@ --- *
+  *
+  * Arguments: @const char *gname@ = trust group @gname@
+  *
+  * Returns:   ---
+  *
+  * Use:               Adds the gid corresponding to @gname@ (which may be a number)
+  *            to the list of things we trust.
+  */
+ static void allowgroup(const char *gname)
+ {
+   struct group *gr;
+   const char *p;
+   gid_t g;
+   /* --- Check for numeric group spec --- */
+   for (p = gname; *p; p++) {
+     if (!isdigit((unsigned char)*p))
+       goto lookup;
+   }
+   g = atoi(gname);
+   goto insert;
+   /* --- Look up a group by name --- */
+ lookup:
+   if ((gr = getgrnam(gname)) == 0)
+     die(1, "group %s not found", gname);
+   g = gr->gr_gid;
+   /* --- Insert the group into the table --- */
+ insert:
+   if (cp.cp_gids >= N(cp.cp_gid))
+     die(1, "too many groups");
+   cp.cp_gid[cp.cp_gids++] = g;
+ }
  /* --- @main@ --- *
   *
   * Arguments: @int argc@ = number of command line arguments
@@@ -276,6 -330,7 +326,7 @@@ int main(int argc, char *argv[]
  {
    int shell = 0;
    int duff = 0;
+   char *p;
  
    enum {
      sh_unknown,
    /* --- Initialize variables --- */
  
    ego(argv[0]);
 -  me = getuid();
 +  me = geteuid();
-   cp.cp_what = CP_WRWORLD | CP_WRGRP | CP_WROTHUSR | CP_STICKYOK;
+   cp.cp_what = (CP_WRWORLD | CP_WROTHGRP | CP_WROTHUSR |
+               CP_STICKYOK | CP_REPORT);
    cp.cp_verbose = 0;
-   cp.cp_report = 0;
+   cp.cp_report = report;
    checkpath_setids(&cp);
+   cp.cp_gids = 0;                     /* ignore group membership */
    pw = getpwuid(me);
    if (!pw)
      die(1, "you don't exist");
        { "usage",      0,              0,      'u' },
        { "bourne",     0,              0,      'b' },
        { "cshell",     0,              0,      'c' },
-       { "verify",     OPTF_ARGREQ,    0,      'v' },
+       { "check",      OPTF_ARGREQ,    0,      'C' },
+       { "verify",     OPTF_ARGREQ,    0,      'C' },
+       { "verbose",    0,              0,      'v' },
+       { "trust-groups",       0,              0,      't' },
+       { "group",      OPTF_ARGREQ,    0,      'g' },
        { 0,            0,              0,      0 }
      };
-     int i = mdwopt(argc, argv, "hVu" "bcv:", opts, 0, 0, 0);
 -    int i = mdwopt(argc, argv, "hVu bcvtg:c:", opts, 0, 0, 0);
++    int i = mdwopt(argc, argv, "hVu" "bcvtg:c:", opts, 0, 0, 0);
  
      if (i < 0)
        break;
        case 'c':
        shell = sh_csh;
        break;
-       case 'v':
+       case 'C':
        return (!fullcheck(optarg));
        break;
+       case 'g':
+       allowgroup(optarg);
+       break;
+       case 'v':
+       cp.cp_verbose++;
+       break;
        default:
        duff = 1;
        break;
    /* --- Choose a shell --- */
  
    if (!shell) {
-     char *p;
      if (!(p = getenv("SHELL")))
        p = pw->pw_shell;
      if (strstr(p, "csh"))
  
    /* --- Start the checking --- */
  
-   {
-     char *p = goodtmp();
-     if (!p)
-       die(1, "no good tmp directory");
-     switch (shell) {
-       case sh_bourne:
-       printf("TMPDIR=\"%s\"; export TMPDIR\n", p);
-       break;
-       case sh_csh:
-       printf("setenv TMPDIR \"%s\"\n", p);
+   if ((p = goodtmp()) == 0)
+     die(1, "no good tmp directory");
+   switch (shell) {
+     case sh_bourne:
+       printf("TMPDIR=\"%s\"; export TMPDIR\n", p);
+       break;
+     case sh_csh:
+       printf("setenv TMPDIR \"%s\"\n", p);
        break;
-     }
-   }
+   }   
  
    return (0);
  }