From: Mark Wooding Date: Sun, 7 Jul 2024 22:49:56 +0000 (+0100) Subject: Add '.ext/cfd/' from commit '4f6b8cb54a4a8487fd08c4feeb4ee150ba53f087' X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/checkpath/commitdiff_plain/18ba890d3c6fb4ee0de17c2a77e1ce5466af9bee?hp=4f6b8cb54a4a8487fd08c4feeb4ee150ba53f087 Add '.ext/cfd/' from commit '4f6b8cb54a4a8487fd08c4feeb4ee150ba53f087' git-subtree-dir: .ext/cfd git-subtree-mainline: 45cecfcfca5c63fb47bfadab781ea6df9f8f4964 git-subtree-split: 4f6b8cb54a4a8487fd08c4feeb4ee150ba53f087 --- diff --git a/.ext/cfd/.skelrc b/.ext/cfd/.skelrc new file mode 100644 index 0000000..3b4e199 --- /dev/null +++ b/.ext/cfd/.skelrc @@ -0,0 +1,9 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Mark Wooding") + (full-title . "the Common Files Distribution (`common')") + (Program . "`Common'") + (program . "`common'")) + skel-alist)) diff --git a/build/auto-version b/.ext/cfd/build/auto-version similarity index 100% rename from build/auto-version rename to .ext/cfd/build/auto-version diff --git a/build/autotest.am b/.ext/cfd/build/autotest.am similarity index 100% rename from build/autotest.am rename to .ext/cfd/build/autotest.am diff --git a/build/confsubst b/.ext/cfd/build/confsubst similarity index 100% rename from build/confsubst rename to .ext/cfd/build/confsubst diff --git a/build/maninst b/.ext/cfd/build/maninst similarity index 100% rename from build/maninst rename to .ext/cfd/build/maninst diff --git a/build/mdwsetup.py b/.ext/cfd/build/mdwsetup.py similarity index 100% rename from build/mdwsetup.py rename to .ext/cfd/build/mdwsetup.py diff --git a/build/pysetup.mk b/.ext/cfd/build/pysetup.mk similarity index 100% rename from build/pysetup.mk rename to .ext/cfd/build/pysetup.mk diff --git a/build/testsuite.at b/.ext/cfd/build/testsuite.at similarity index 100% rename from build/testsuite.at rename to .ext/cfd/build/testsuite.at diff --git a/doc/INSTALL b/.ext/cfd/doc/INSTALL similarity index 100% rename from doc/INSTALL rename to .ext/cfd/doc/INSTALL diff --git a/doc/texinice.tex b/.ext/cfd/doc/texinice.tex similarity index 100% rename from doc/texinice.tex rename to .ext/cfd/doc/texinice.tex diff --git a/licence/AGPL-3 b/.ext/cfd/licence/AGPL-3 similarity index 100% rename from licence/AGPL-3 rename to .ext/cfd/licence/AGPL-3 diff --git a/licence/GPL-1 b/.ext/cfd/licence/GPL-1 similarity index 100% rename from licence/GPL-1 rename to .ext/cfd/licence/GPL-1 diff --git a/licence/GPL-2 b/.ext/cfd/licence/GPL-2 similarity index 100% rename from licence/GPL-2 rename to .ext/cfd/licence/GPL-2 diff --git a/licence/GPL-3 b/.ext/cfd/licence/GPL-3 similarity index 100% rename from licence/GPL-3 rename to .ext/cfd/licence/GPL-3 diff --git a/licence/LGPL-2 b/.ext/cfd/licence/LGPL-2 similarity index 100% rename from licence/LGPL-2 rename to .ext/cfd/licence/LGPL-2 diff --git a/licence/LGPL-2.1 b/.ext/cfd/licence/LGPL-2.1 similarity index 100% rename from licence/LGPL-2.1 rename to .ext/cfd/licence/LGPL-2.1 diff --git a/licence/LGPL-3 b/.ext/cfd/licence/LGPL-3 similarity index 100% rename from licence/LGPL-3 rename to .ext/cfd/licence/LGPL-3 diff --git a/licence/agpl-3.0.tex b/.ext/cfd/licence/agpl-3.0.tex similarity index 100% rename from licence/agpl-3.0.tex rename to .ext/cfd/licence/agpl-3.0.tex diff --git a/licence/agpl-3.0.texi b/.ext/cfd/licence/agpl-3.0.texi similarity index 100% rename from licence/agpl-3.0.texi rename to .ext/cfd/licence/agpl-3.0.texi diff --git a/licence/gpl-2.0.tex b/.ext/cfd/licence/gpl-2.0.tex similarity index 100% rename from licence/gpl-2.0.tex rename to .ext/cfd/licence/gpl-2.0.tex diff --git a/licence/gpl-2.0.texi b/.ext/cfd/licence/gpl-2.0.texi similarity index 100% rename from licence/gpl-2.0.texi rename to .ext/cfd/licence/gpl-2.0.texi diff --git a/licence/gpl-3.0.tex b/.ext/cfd/licence/gpl-3.0.tex similarity index 100% rename from licence/gpl-3.0.tex rename to .ext/cfd/licence/gpl-3.0.tex diff --git a/licence/gpl-3.0.texi b/.ext/cfd/licence/gpl-3.0.texi similarity index 100% rename from licence/gpl-3.0.texi rename to .ext/cfd/licence/gpl-3.0.texi diff --git a/licence/gpl.texi b/.ext/cfd/licence/gpl.texi similarity index 100% rename from licence/gpl.texi rename to .ext/cfd/licence/gpl.texi diff --git a/licence/latex-licence-test.tex b/.ext/cfd/licence/latex-licence-test.tex similarity index 100% rename from licence/latex-licence-test.tex rename to .ext/cfd/licence/latex-licence-test.tex diff --git a/licence/lgpl-2.0.tex b/.ext/cfd/licence/lgpl-2.0.tex similarity index 100% rename from licence/lgpl-2.0.tex rename to .ext/cfd/licence/lgpl-2.0.tex diff --git a/licence/lgpl-2.0.texi b/.ext/cfd/licence/lgpl-2.0.texi similarity index 100% rename from licence/lgpl-2.0.texi rename to .ext/cfd/licence/lgpl-2.0.texi diff --git a/licence/lgpl-2.1.tex b/.ext/cfd/licence/lgpl-2.1.tex similarity index 100% rename from licence/lgpl-2.1.tex rename to .ext/cfd/licence/lgpl-2.1.tex diff --git a/licence/lgpl-2.1.texi b/.ext/cfd/licence/lgpl-2.1.texi similarity index 100% rename from licence/lgpl-2.1.texi rename to .ext/cfd/licence/lgpl-2.1.texi diff --git a/licence/lgpl-3.0.tex b/.ext/cfd/licence/lgpl-3.0.tex similarity index 100% rename from licence/lgpl-3.0.tex rename to .ext/cfd/licence/lgpl-3.0.tex diff --git a/licence/lgpl-3.0.texi b/.ext/cfd/licence/lgpl-3.0.texi similarity index 100% rename from licence/lgpl-3.0.texi rename to .ext/cfd/licence/lgpl-3.0.texi diff --git a/licence/texinfo-licence-test.texi b/.ext/cfd/licence/texinfo-licence-test.texi similarity index 100% rename from licence/texinfo-licence-test.texi rename to .ext/cfd/licence/texinfo-licence-test.texi diff --git a/m4/mdw-auto-version.m4 b/.ext/cfd/m4/mdw-auto-version.m4 similarity index 100% rename from m4/mdw-auto-version.m4 rename to .ext/cfd/m4/mdw-auto-version.m4 diff --git a/m4/mdw-decl-environ.m4 b/.ext/cfd/m4/mdw-decl-environ.m4 similarity index 100% rename from m4/mdw-decl-environ.m4 rename to .ext/cfd/m4/mdw-decl-environ.m4 diff --git a/m4/mdw-define-paths.m4 b/.ext/cfd/m4/mdw-define-paths.m4 similarity index 100% rename from m4/mdw-define-paths.m4 rename to .ext/cfd/m4/mdw-define-paths.m4 diff --git a/m4/mdw-dir-texmf.m4 b/.ext/cfd/m4/mdw-dir-texmf.m4 similarity index 100% rename from m4/mdw-dir-texmf.m4 rename to .ext/cfd/m4/mdw-dir-texmf.m4 diff --git a/m4/mdw-libtool-version-info.m4 b/.ext/cfd/m4/mdw-libtool-version-info.m4 similarity index 100% rename from m4/mdw-libtool-version-info.m4 rename to .ext/cfd/m4/mdw-libtool-version-info.m4 diff --git a/m4/mdw-manext.m4 b/.ext/cfd/m4/mdw-manext.m4 similarity index 100% rename from m4/mdw-manext.m4 rename to .ext/cfd/m4/mdw-manext.m4 diff --git a/m4/mdw-silent-rules.m4 b/.ext/cfd/m4/mdw-silent-rules.m4 similarity index 100% rename from m4/mdw-silent-rules.m4 rename to .ext/cfd/m4/mdw-silent-rules.m4 diff --git a/src/getdate.h b/.ext/cfd/src/getdate.h similarity index 100% rename from src/getdate.h rename to .ext/cfd/src/getdate.h diff --git a/src/getdate.y b/.ext/cfd/src/getdate.y similarity index 100% rename from src/getdate.y rename to .ext/cfd/src/getdate.y diff --git a/src/mdwopt.c b/.ext/cfd/src/mdwopt.c similarity index 100% rename from src/mdwopt.c rename to .ext/cfd/src/mdwopt.c diff --git a/src/mdwopt.h b/.ext/cfd/src/mdwopt.h similarity index 100% rename from src/mdwopt.h rename to .ext/cfd/src/mdwopt.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..160b512 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/COPYING + +/Makefile.in +/configure + +/aclocal.m4 +/autom4te.cache/ +/config/ diff --git a/.links b/.links new file mode 100644 index 0000000..5a93e8f --- /dev/null +++ b/.links @@ -0,0 +1,3 @@ +COPYING +config/auto-version +config/confsubst diff --git a/.skelrc b/.skelrc index 3b4e199..71fd9df 100644 --- a/.skelrc +++ b/.skelrc @@ -2,8 +2,7 @@ (setq skel-alist (append - '((author . "Mark Wooding") - (full-title . "the Common Files Distribution (`common')") - (Program . "`Common'") - (program . "`common'")) + '((full-title . "chkpath") + (program . "chkpath") + (author . "Mark Wooding")) skel-alist)) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..9785ae1 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,115 @@ +### -*-makefile-*- +### +### Makefile for chkpath +### +### (c) 1999 Mark Wooding +### + +###----- Licensing notice --------------------------------------------------- +### +### This file is part of chkpath. +### +### chkpath is free software; you can redistribute it and/or modify +### 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. + +bin_PROGRAMS = +lib_LTLIBRARIES = +noinst_LIBRARIES = +include_HEADERS = +dist_man_MANS = + +CLEANFILES = +EXTRA_DIST = +LDADD = + +###-------------------------------------------------------------------------- +### Programs. + +## Common stuff. +noinst_LIBRARIES += libutils.a +libutils_a_SOURCES = +LDADD += libutils.a + +libutils_a_SOURCES += utils.c +libutils_a_SOURCES += utils.h + +## chkpath +bin_PROGRAMS += chkpath +dist_man_MANS += chkpath.1 + +## tmpdir +bin_PROGRAMS += tmpdir +dist_man_MANS += tmpdir.1 + +###-------------------------------------------------------------------------- +### Library. + +## The library itself. +lib_LTLIBRARIES += libcheckpath.la +libcheckpath_la_SOURCES = +libcheckpath_la_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO) +libcheckpath_la_LIBADD = $(mLib_LIBS) +dist_man_MANS += checkpath.3 +LDADD += libcheckpath.la + +libcheckpath_la_SOURCES += checkpath.c +include_HEADERS += checkpath.h + +## Package description. +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = checkpath.pc +CLEANFILES += checkpath.pc +EXTRA_DIST += checkpath.pc.in + +checkpath.pc: checkpath.pc.in Makefile + $(AM_V_GEN)$(top_srcdir)/config/confsubst \ + $(srcdir)/checkpath.pc.in >$@.new \ + prefix=$(prefix) exec_prefix=$(exec_prefix) \ + libdir=$(libdir) includedir=$(includedir) \ + VERSION=$(VERSION) && mv $@.new $@ + +###-------------------------------------------------------------------------- +### Other finishing touches. + +## Version stamp. +dist-hook:: + echo $(VERSION) >$(distdir)/RELEASE + +## Build tools. +EXTRA_DIST += config/auto-version +EXTRA_DIST += config/confsubst + +## External libraries. +LDADD += $(mLib_LIBS) + +###-------------------------------------------------------------------------- +### Debian. + +## Main stuff. +EXTRA_DIST += debian/rules +EXTRA_DIST += debian/copyright +EXTRA_DIST += debian/control +EXTRA_DIST += debian/changelog +EXTRA_DIST += debian/compat +EXTRA_DIST += debian/source/format + +## Library. +EXTRA_DIST += debian/libcheckpath1.install +EXTRA_DIST += debian/libcheckpath-dev.install + +## Binaries. +EXTRA_DIST += debian/tmpdir.install +EXTRA_DIST += debian/chkpath.install + +###----- That's all, folks -------------------------------------------------- diff --git a/checkpath.3 b/checkpath.3 new file mode 100644 index 0000000..921dec7 --- /dev/null +++ b/checkpath.3 @@ -0,0 +1,207 @@ +.\" -*-nroff-*- +.TH checkpath 3 "25 January 2003" "Local tools" +.SH NAME +checkpath \- library for examining path security +.SH SYNOPSIS +.nf +.B "#include " + +.BI "int checkpath(const char *" path , +.BI " const struct checkpath *" cp ");" +.BI "int checkpath_addgid(struct checkpath *" cp ", gid_t " g ");" +.BI "void checkpath_setuid(struct checkpath *" cp ");" +.BI "int checkpath_setgid(struct checkpath *" cp ");" +.BI "int checkpath_setgroups(struct checkpath *" cp ");" +.BI "void checkpath_setids(struct checkpath *" cp ");" +.fi +.SH DESCRIPTION +The +.B checkpath +function checks a path for security. It ensures that only acceptble +users and groups can change the files or file contents accessible +through the path. +.PP +The function is given a +.I path +to be checked, and a pointer +.I cp +to a parameter block of type +.BR "struct checkpath" . +It scans the path and returns a bitmask of problems that it found. It +may also have invoked a caller-provided reporting function with details +of the problems. +.SS "The parameter structure" +This structure contains the following members: +.TP +.B "uid_t cp_uid" +The user running the check. Files and directories owned by +.B root +(uid 0) and by +.B cp_uid +are considered safe. +.TP +.B "gid_t cp_gid[NGROUPS_MAX + 1]" +The groups of which the user is a member. Files whose groups are in +this set may be considered safe, depending on the +.B cp_what +configuration. See below. +.TP +.B "int cp_gids" +The number of gids in the +.B cp_gid +array. +.TP +.B "int cp_verbose" +The verbosity level. Messages are only given to the reporting function +if their verbosity level is less than or equal to this setting. As a +guide, unexpected errors (e.g., nonexistent files) have level 0, +security concerns about the path have level 1, and other details have +levels 2 and above. The recommended value is 1. +.TP +.B "unsigned cp_what" +A bitmask of flags determining what conditions are considered problems, +and other behaviour. See below. +.TP +.B "void (*cp_report)(...)" +The reporting function. See below. +.TP +.B "void *cp_arg" +An argument to be passed to the reporting function +.BR cp_report . +.PP +The members +.BR cp_uid , +.B cp_gid +and +.B cp_gids +can be set up to reflect the current user and group memberships by +calling +.B checkpath_setids +passing the address of the structure to fill in; this overwrites the +previous contents of the +.BR cp_uid , +.BR cp_gid , +and +.BR cp_gids +members. Alternatively, the functions +.BR checkpath_setuid , +.BR checkpath_setgid , +and +.B checkpath_setgroups +can be called to do the job in stages. The +.B checkpath_setuid +function simply stores the process's real uid in +.BR cp_uid . +The +.B checkpath_setgid +and +.B checkpath_setgroups +functions store respectively the process's real gid and supplementary +gids in the +.B cp_gid +array; they avoid inserting duplicate entries; they return 0 on success +or \-1 if the table would overflow; they assume that the table is +properly set up (minimally, just set +.BR "cp_gids = 0" ). +They use a utility function +.B checkpath_addgid +to do their work; it takes a pointer to a +.B struct checkpath +and a gid, and again returns 0 on success or \-1 if overflow would +occur. +.SS "The cp_what flags" +The +.B cp_what +flags are as follows. +.TP +.B CP_ERROR +Some kind of operating system error occurred while checking the path. +.TP +.B CP_WRWORLD +A path element is world writable. +.TP +.B CP_WRGRP +A path element is group-writable. +.TP +.B CP_WROTHGRP +A path element is group-writable, and its group is not listed in +.BR cp_gid . +.TP +.B CP_WROTHUSR +A path element is owned by another user. +.TP +.B CP_SYMLINK +Report traversal of a symbolic link while checking the path. +.TP +.B CP_REPORT +Format a user-readable message string when reporting problems. +.TP +.B CP_STICKYOK +Don't complain if the object designated by the path is a sticky +directory, as long as the owner is trustworthy (i.e., either +.B root +or +.BR cp_uid ). +This is useful when testing candidate temporary directories for +suitability. +.PP +The flags +.BR CP_ERROR , +.BR CP_WRWORLD , +.BR CP_WRGRP , +.BR CP_WROTHGRP , +and +.B CP_WROTHUSR +are collectively the +.I problem +flags. The mask +.B CP_PROBLEMS +has only these bits set. A problem is reported (and returned) only if +its bit is set in the +.B cp_what +structure member. Note that it's possible that a problem might fit into +multiple categories, e.g., a group-writable directory might be reported +as +.B CP_WRGRP +or +.BR CP_WROTHGRP ; +in this case, the most specific problem is used (in this case +.BR CP_WROTHGRP ). +.SS "The reporting function" +The reporting function is passed the following arguments: +.TP +.BI "unsigned " what +A flag inidicating the kind of notification this is. It will be a +problem flag or +.BR CP_SYMLINK . +.TP +.BI "int " verb +The verbosity level of this message. +.TP +.BI "const char *" path +The full pathname of the object which caused this report to be issued. +The pathname will not contain symbolic links (except as the last +element, and then only if this is a +.B CP_SYMLINK +notification). +.TP +.BI "const char *" msg +A human-readable message describing this notification in detail. This +is only generated if +.B CP_REPORT +is set in the +.B cp_what +flags. +.TP +.BI "void *" p +The +.B cp_arg +member of the parameter structure, provided as a context pointer for the +reporting function. +.SH BUGS +None known. +.SH SEE ALSO +.BR chkpath (1), +.BR tmpdir (1). +.SH AUTHOR +Mark Wooding (mdw@nsict.org). diff --git a/checkpath.c b/checkpath.c new file mode 100644 index 0000000..e594201 --- /dev/null +++ b/checkpath.c @@ -0,0 +1,583 @@ +/* -*-c-*- + * + * Check a path for safety + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "checkpath.h" + +/*----- Data structures ---------------------------------------------------*/ + +/* --- An item in the directory list --- * + * + * Each directory becomes an element on a list which is manipulated in a + * stack-like way. + */ + +struct elt { + struct elt *e_link; /* Pointer to the next one along */ + size_t e_offset; /* Offset of name in path string */ + unsigned e_flags; /* Various useful flags */ + char e_name[1]; /* Name of the directory */ +}; + +#define f_sticky 1u /* Directory has sticky bit set */ + +#define f_last 1u /* This is the final item to check */ + +/*----- Static variables --------------------------------------------------*/ + +static struct elt rootnode = { 0, 0, 0 }; /* Root of the list */ +static struct elt *sp; /* Stack pointer for list */ +static dstr d = DSTR_INIT; /* Current path string */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @splitpath@ --- * + * + * Arguments: @const char *path@ = path string to break apart + * @struct elt *tail@ = tail block to attach to end of list + * + * Returns: Pointer to the new list head. + * + * Use: Breaks a path string into directories and adds each one + * as a node on the list, in the right order. These can then + * be pushed onto the directory stack as required. + */ + +static struct elt *splitpath(const char *path, struct elt *tail) +{ + struct elt *head, **ee = &head, *e; + + while (*path) { + size_t n; + + /* --- Either a leading `/', or a doubled one --- * + * + * Either way, ignore it. + */ + + if (*path == '/') { + path++; + continue; + } + + /* --- Skip to the next directory separator --- * + * + * Build a list element for it, and link it on. + */ + + n = strcspn(path, "/"); + e = xmalloc(sizeof(struct elt) + n + 1); + memcpy(e->e_name, path, n); + e->e_name[n] = 0; + e->e_flags = 0; + *ee = e; + ee = &e->e_link; + path += n; + } + + /* --- Done --- */ + + *ee = tail; + return (head); +} + +/* --- @pop@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Removes the top item from the directory stack. + */ + +static void pop(void) +{ + if (sp->e_link) { + struct elt *e = sp->e_link; + d.len = sp->e_offset; + DPUTZ(&d); + sp = e; + } +} + +/* --- @popall@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Removes all the items from the directory stack. + */ + +static void popall(void) + { while (sp->e_link) pop(); } + +/* --- @push@ --- * + * + * Arguments: @struct elt *e@ = pointer to directory element + * + * Returns: --- + * + * Use: Pushes a new subdirectory onto the stack. + */ + +static void push(struct elt *e) +{ + e->e_link = sp; + e->e_offset = d.len; + DPUTC(&d, '/'); + DPUTS(&d, e->e_name); + sp = e; +} + +/* --- @report@ --- * + * + * Arguments: @const struct checkpath *cp@ = pointer to context + * @unsigned what@ = what sort of report is this? + * @int verbose@ = how verbose is this? + * @const char *p@ = what path does it refer to? + * @const char *msg@ = the message to give to the user + * + * Returns: --- + * + * Use: Formats and presents messages to the client. + */ + +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 --- */ + + 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); +} + +/* --- @sanity@ --- * + * + * Arguments: @const char *p@ = name of directory to check + * @struct stat *st@ = pointer to @stat@(2) block for it + * @const struct checkpath *cp@ = pointer to caller parameters + * @unsigned f@ = various flags + * + * Returns: Zero if everything's OK, else bitmask of problems. + * + * Use: Performs the main load of sanity-checking on a directory. + */ + +static unsigned sanity(const char *p, struct stat *st, + const struct checkpath *cp, unsigned f) +{ + unsigned bad = 0; + int stickyok = 0; + int i; + unsigned b; + + if (S_ISDIR(st->st_mode) && + (!(f & f_last) || (cp->cp_what & CP_STICKYOK))) + stickyok = 01000; + + /* --- Check for world-writability --- */ + + if ((cp->cp_what & CP_WRWORLD) && + (st->st_mode & (0002 | stickyok)) == 0002) { + bad |= CP_WRWORLD; + report(cp, CP_WRWORLD, 1, p, "** world writable **"); + } + + /* --- Check for group-writability --- */ + + if ((cp->cp_what & (CP_WRGRP | CP_WROTHGRP)) && + (st->st_mode & (0020 | stickyok)) == 0020) { + b = CP_WRGRP; + + if (cp->cp_what & CP_WROTHGRP) { + b = CP_WROTHGRP; + for (i = 0; i < cp->cp_gids; i++) { + if (st->st_gid == cp->cp_gid[i]) + b = cp->cp_what & CP_WRGRP; + } + } + if (b) { + bad |= b; + report(cp, b, 1, p, "writable by %sgroup %g", + (b == CP_WROTHGRP) ? "other " : "", st->st_gid); + } + } + + /* --- Check for user-writability --- */ + + if ((cp->cp_what & CP_WROTHUSR) && + st->st_uid != cp->cp_uid && + st->st_uid != 0) { + bad |= CP_WROTHUSR; + report(cp, CP_WROTHUSR, 1, p, "owner is user %u", st->st_uid); + } + + /* --- Done sanity check --- */ + + return (bad); +} + +/* --- @checkpath@ --- * + * + * Arguments: @const char *p@ = directory name which needs checking + * @const struct checkpath *cp@ = parameters for the check + * + * Returns: Zero if all is well, otherwise bitmask of problems. + * + * Use: Scrutinises a directory path to see what evil things other + * users could do to it. + */ + +unsigned checkpath(const char *p, const struct checkpath *cp) +{ + char cwd[PATH_MAX]; + struct elt *e, *ee; + struct stat st; + unsigned bad = 0; + + /* --- Initialize stack pointer and path string --- */ + + sp = &rootnode; + dstr_destroy(&d); + + /* --- Try to find the current directory --- */ + + if (!getcwd(cwd, sizeof(cwd))) { + report(cp, CP_ERROR, 0, 0, "can't find current directory: %e"); + return (CP_ERROR); + } + + /* --- Check that the root directory is OK --- */ + + if (stat("/", &st)) { + report(cp, CP_ERROR, 0, 0, "can't stat root directory: %e"); + return (CP_ERROR); + } + + report(cp, CP_REPORT, 3, p, "begin scan"); + bad |= sanity("/", &st, cp, 0); + + /* --- Get the initial list of things to process --- */ + + ee = splitpath(p, 0); + if (*p != '/') + ee = splitpath(cwd, ee); + + /* --- While there are list items which still need doing --- */ + + while (ee) { + e = ee->e_link; + + /* --- Strip off simple `.' elements --- */ + + if (strcmp(ee->e_name, ".") == 0) { + free(ee); + ee = e; + continue; + } + + /* --- Backtrack on `..' elements --- */ + + else if (strcmp(ee->e_name, "..") == 0) { + pop(); + free(ee); + ee = e; + continue; + } + + /* --- Everything else gets pushed on the end --- */ + + push(ee); + ee = e; + + /* --- Find out what sort of a thing this is --- */ + + if (lstat(d.buf, &st)) { + report(cp, CP_ERROR, 0, d.buf, "can't stat: %e"); + bad |= CP_ERROR; + break; + } + + /* --- Handle symbolic links specially --- */ + + if (S_ISLNK(st.st_mode)) { + dstr buf = DSTR_INIT; + int i; + + /* --- Resolve the link --- */ + + dstr_ensure(&buf, st.st_size + 1); + if ((i = readlink(d.buf, buf.buf, buf.sz)) < 0) { + report(cp, CP_ERROR, 0, d.buf, "can't readlink: %e"); + bad |= CP_ERROR; + break; + } + buf.buf[i] = 0; + report(cp, CP_SYMLINK, 2, d.buf, "symlink -> `%s'", buf.buf); + + /* --- Handle sticky parents --- * + * + * If I make a symlink in a sticky directory, I can later modify it. + * However, nobody else can (except the owner of the directory, and + * we'll already have noticed that if we care). + */ + + if ((cp->cp_what & CP_WROTHUSR) && + (sp->e_link->e_flags & f_sticky) && + st.st_uid != cp->cp_uid && st.st_uid != 0) { + bad |= CP_WROTHUSR; + report(cp, CP_WROTHUSR, 1, d.buf, + "symlink modifiable by user %u", st.st_uid); + } + + /* --- Sort out what to do from here --- */ + + if (buf.buf[0] == '/') + popall(); + else + pop(); + ee = splitpath(buf.buf, ee); + dstr_destroy(&buf); + continue; + } + + /* --- Run the sanity check on this path element --- */ + + bad |= sanity(d.buf, &st, cp, ee ? 0 : f_last); + + if (S_ISDIR(st.st_mode)) { + if (st.st_mode & 01000) + sp->e_flags |= f_sticky; + report(cp, CP_REPORT, 4, d.buf, "directory"); + continue; + } + + /* --- Something else I don't understand --- */ + + break; + } + + /* --- Check for leftover junk --- */ + + if (ee) { + if (!(bad & CP_ERROR)) + report(cp, CP_ERROR, 0, 0, "junk left over after reaching leaf"); + while (ee) { + e = ee->e_link; + free(ee); + ee = e; + } + } + + popall(); + return (bad); +} + +/* --- @checkpath_addgid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * @gid_t g@ = group id to add + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the group @g@ to the structure. + */ + +int checkpath_addgid(struct checkpath *cp, gid_t g) +{ + int i; + + for (i = 0; i < cp->cp_gids; i++) { + if (cp->cp_gid[i] == g) + return (0); + } + if (cp->cp_gids >= N(cp->cp_gid)) + return (-1); + cp->cp_gid[cp->cp_gids++] = g; + return (0); +} + +/* --- @checkpath_setuid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: --- + * + * Use: Fills in the @cp_uid@ slot of the structure with the real uid + * of the current process. + */ + +void checkpath_setuid(struct checkpath *cp) { cp->cp_uid = getuid(); } + +/* --- @checkpath_setgid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the real gid of the current process to the @cp_gid@ + * array. + */ + +int checkpath_setgid(struct checkpath *cp) + { return (checkpath_addgid(cp, getgid())); } + +/* --- @checkpath_setgroups@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the current process's supplementary groups to the + * @cp_gid@ table. + */ + +int checkpath_setgroups(struct checkpath *cp) +{ + int i, n; + gid_t gg[NGROUPS_MAX]; + + n = getgroups(N(gg), gg); + for (i = 0; i < n; i++) { + if (checkpath_addgid(cp, gg[i])) + return (-1); + } + return (0); +} + +/* --- @checkpath_setids@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: --- + * + * Use: Fills in the user ids and things in the structure. This is + * equivalent to setting @cp_gids = 0@ and then calling + * @_setuid@, @_setgid@ and @_setgroups@. It can't fail. + */ + +void checkpath_setids(struct checkpath *cp) +{ + cp->cp_gids = 0; + checkpath_setuid(cp); + checkpath_setgid(cp); + checkpath_setgroups(cp); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/checkpath.h b/checkpath.h new file mode 100644 index 0000000..5b2e938 --- /dev/null +++ b/checkpath.h @@ -0,0 +1,156 @@ +/* -*-c-*- + * + * Check a path for safety + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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. + */ + +#ifndef CHECKPATH_H +#define CHECKPATH_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Search request --- * + * + * This contains parameters from the caller to control what problems are + * looked for, and what to do when they're found. + */ + +struct checkpath { + uid_t cp_uid; /* Uid that's considered OK */ + gid_t cp_gid[NGROUPS_MAX + 1]; /* Array of groups that are OK */ + int cp_gids; /* Number of groups in the array */ + int cp_verbose; /* Verbosity level to spit up */ + unsigned cp_what; /* What things to check for */ + void (*cp_report)(unsigned /*what*/, int /*verb*/, + const char */*dir*/, const char */*msg*/, + void */*p*/); + void *cp_arg; /* Argument for cp_report */ +}; + +/* --- Flags for `@what@' fields in the above --- */ + +/* Problem types */ +#define CP_PROBLEMS 0x1fu /* Mask of problem bits */ +#define CP_ERROR 0x01u /* Error report */ +#define CP_WRWORLD 0x02u /* Check write by world */ +#define CP_WRGRP 0x04u /* Check write by any group */ +#define CP_WROTHGRP 0x08u /* Check write by other group */ +#define CP_WROTHUSR 0x10u /* Check write by other user */ + +/* Other flags */ +#define CP_SYMLINK 0x100u /* Report symbolic links */ +#define CP_REPORT 0x200u /* Make user-readable reports */ +#define CP_STICKYOK 0x400u /* Don't care if sticky is set */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @checkpath@ --- * + * + * Arguments: @const char *p@ = directory name which needs checking + * @const struct checkpath *cp@ = parameters for the check + * + * Returns: Zero if all is well, otherwise bitmask of problems. + * + * Use: Scrutinises a directory path to see what evil things other + * users could do to it. + */ + +extern unsigned checkpath(const char */*p*/, const struct checkpath */*cp*/); + +/* --- @checkpath_addgid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * @gid_t g@ = group id to add + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the group @g@ to the structure. + */ + +extern int checkpath_addgid(struct checkpath */*cp*/, gid_t /*g*/); + +/* --- @checkpath_setuid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: --- + * + * Use: Fills in the @cp_uid@ slot of the structure with the real uid + * of the current process. + */ + +extern void checkpath_setuid(struct checkpath */*cp*/); + +/* --- @checkpath_setgid@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the real gid of the current process to the @cp_gid@ + * array. + */ + +extern int checkpath_setgid(/*cp*/); + +/* --- @checkpath_setgroups@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: Zero if successful, nonzero if the array is full. + * + * Use: Adds the current process's supplementary groups to the + * @cp_gid@ table. + */ + +extern int checkpath_setgroups(struct checkpath */*cp*/); + +/* --- @checkpath_setids@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to block to fill in + * + * Returns: --- + * + * Use: Fills in the user ids and things in the structure. This is + * equivalent to setting @cp_gids = 0@ and then calling + * @_setuid@, @_setgid@ and @_setgroups@. It can't fail. + */ + +extern void checkpath_setids(struct checkpath */*cp*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/checkpath.pc.in b/checkpath.pc.in new file mode 100644 index 0000000..d9822d9 --- /dev/null +++ b/checkpath.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +configdir=@configdir@ +socketdir=@socketdir@ + +Name: Checkpath +Description: Check filesystem paths for security problems. +Version: @VERSION@ + +Requires: mLib >= 2.0.4 +Libs: -L${libdir} -lcheckpath +Cflags: -I${includedir} diff --git a/chkpath.1 b/chkpath.1 new file mode 100644 index 0000000..181cc09 --- /dev/null +++ b/chkpath.1 @@ -0,0 +1,127 @@ +.\" -*-nroff-*- +.TH chkpath 1 "6 April 1999" "Local tools" +.SH NAME +chkpath \- check a path string for security +.SH SYNOPSIS +.B chkpath +.RB [ \-vqstp ] +.RB [ \-g +.IR group ] +.RI [ path ...] +.SH USAGE +The +.B chkpath +command checks one or more path strings (i.e., lists of directories +separated by colons) for security. If no path strings are given, the +value of the +.B PATH +environment variable is examined. +.PP +Each directory in turn is broken into its constituent parts and every +step which must be made through the filesystem to reach that directory +from the root is scrutinized for vulnerabilities. The checks made +against each directory and symbolic link along the way are as follows: +.IP " 1." +No step should be a directory which is world-writable unless its sticky +bit is set, and it's not the final step. +.IP " 2." +No step should be a directory which is group-writable unless its sticky +bit is set, and it's not the final step. (However, see the +.B \-t +option below.) +.IP " 3." +No step should be a directory owned by another user (other than root). +.IP " 4." +No step should be a symbolic link inside a sticky directory and owned by +another user. +.PP +The author is not aware of any weaknesses in this ruleset. The +objective is that nobody other than the user and the superuser should be +able to add or change the set of files available within the directories +of the path(s). +.SS Options +The following command line options are available: +.TP +.B "\-h, \-\-help" +Displays a relatively verbose message describing how to use +.BR chkpath . +.TP +.B "\-V, \-\-version" +Displays +.BR chkpath 's +version number. +.TP +.B "\-u, \-\-usage" +Displays a very terse usage summary. +.TP +.B "\-v, \-\-verbose" +Makes +.B chkpath +more verbose about what it's doing. This option has a cumulative +effect, so put more in for more verbosity. Note that verbose doesn't +mean the same as interesting. The default is to report problems with +directories and system errors. +.TP +.B "\-g, \-\-group " group +Consider members of +.I group +to be trustworthy: +.B chkpath +won't warn about a directory being group-writable if its gid matches +.IR group . +The +.I group +may be a group name (looked up in +.BR /etc/group ) +or a numeric gid in decimal. +.TP +.B "\-q, \-\-quiet" +Makes +.B chkpath +less verbose about what it's doing. This option, like +.BR \-v , +has a cumulative effect. Each +.B \-q +cancels out a +.B \-v +option. +.TP +.B "\-s, \-\-sticky" +Modifies the ruleset slightly so that any step through the filesystem is +OK, even if world- or group-writable (but not owned by someone else), as +long as the directory's sticky bit is set. The default is that sticky +directories are considered safe only if they're not the final step. +Turning this option on isn't recommended: if you use a sticky directory +in your path then other people can add malicious commands whose names +are common typos of standard ones. +.TP +.B "\-t, \-\-trust\-group" +Modifies the ruleset slightly so that +.B chkpath +doesn't warn about directories group-owned by groups you're a member +of. In other words, it trusts your fellow group-members +.IR "in their capacity as group-owners only" : +.B chkpath +will still warn about directories owned by people in your groups. +.TP +.B "\-p, \-\-print" +Writes on standard output a colon-separated list of the directories +which +.B chkpath +considered `safe'. This can be used to filter out unsafe directories in +an automatic way: +.RS 10 +.nf +.ft B +.sp 1 +PATH=`chkpath -qqp` +.ft R +.fi +.RE +.SH BUGS +None known. +.SH SEE ALSO +.BR tmpdir (1), +.BR checkpath (3). +.SH AUTHOR +Mark Wooding (mdw@nsict.org). diff --git a/chkpath.c b/chkpath.c new file mode 100644 index 0000000..a44570e --- /dev/null +++ b/chkpath.c @@ -0,0 +1,212 @@ +/* -*-c-*- + * + * Check a user's file search path + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "checkpath.h" +#include "utils.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @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) + { fprintf(fp, "Usage: %s [-vqstp] [-g NAME] [PATH...]\n", QUIS); } + +/* --- @version@ --- */ + +static void version(FILE *fp) + { fprintf(fp, "%s version %s\n", QUIS, VERSION); } + +/* --- @help@ --- */ + +static void help(FILE *fp) +{ + version(fp); + putc('\n', fp); + usage(fp); + fputs("\n\ +Checks a path string (by default the PATH variable) for security. It\n\ +ensures that only `root' or the calling user can write to all the parent\n\ +directories of the path elements, so nobody can maliciously replace the\n\ +binaries unexpectedly.\n\ +\n\ +Options provided are:\n\ +\n\ +-h, --help Display this help message.\n\ +-V, --version Display the program's version number.\n\ +-u, --usage Show a terse usage summary.\n\ +\n\ +-v, --verbose Be verbose about the search progress (cumulative).\n\ +-q, --quiet Be quiet about the search progress (cumulative).\n\ +-s, --sticky Consider sticky directories secure against\n\ + modification by world and group (not recommended).\n\ +-t, --trust-group Consider other members of your group trustworthy.\n\ +-g, --group NAME Consider members of group NAME trustworthy.\n\ +-p, --print Write the secure path elements to standard output.\n\ +", + fp); +} + +int main(int argc, char *argv[]) +{ + unsigned bad = 0; + int i; + char *p, *q, *path; + struct checkpath cp; + int f = 0; + +#define f_print 1u +#define f_colon 2u + + /* --- Initialize the world --- */ + + ego(argv[0]); + + /* --- Set up path scanning defaults --- */ + + cp.cp_verbose = 1; + cp.cp_what = (CP_PROBLEMS | CP_REPORT | CP_SYMLINK) & ~CP_WRGRP; + cp.cp_report = report; + cp.cp_arg = 0; + cp.cp_gids = 0; + checkpath_setuid(&cp); + + /* --- Parse the options --- */ + + for (;;) { + static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "usage", 0, 0, 'u' }, + { "verbose", 0, 0, 'v' }, + { "quiet", 0, 0, 'q' }, + { "sticky", 0, 0, 's' }, + { "trust-group", 0, 0, 't' }, + { "print", 0, 0, 'p' }, + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "hVu" "vqstpg:", opts, 0, 0, 0); + + if (i < 0) + break; + switch (i) { + case 'h': + help(stdout); + exit(0); + case 'V': + version(stdout); + exit(0); + case 'u': + usage(stdout); + exit(0); + case 'v': + cp.cp_verbose++; + break; + case 'q': + if (cp.cp_verbose) + cp.cp_verbose--; + break; + case 's': + cp.cp_what |= CP_STICKYOK; + break; + case 't': + if (checkpath_setgid(&cp) || checkpath_setgroups(&cp)) + die(1, "too many groups"); + break; + case 'g': + allowgroup(&cp, optarg); + break; + case 'p': + f |= f_print; + break; + default: + bad = 1; + break; + } + } + + if (bad) { + usage(stderr); + exit(1); + } + + /* --- Sort out what needs doing --- */ + + if (optind == argc) { + path = getenv("PATH"); + argv = &path; + argc = 1; + optind = 0; + } + + for (i = optind; i < argc; i++) { + p = xstrdup(argv[i]); + q = strtok(p, ":"); + while (q) { + unsigned b = checkpath(q, &cp); + if (!b && (f & f_print)) { + if (f & f_colon) + putchar(':'); + fputs(q, stdout); + f |= f_colon; + } + bad |= b; + q = strtok(0, ":"); + } + free(p); + } + + if (f & f_colon) + putchar('\n'); + + return (bad); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..759333e --- /dev/null +++ b/configure.ac @@ -0,0 +1,58 @@ +dnl -*-autoconf-*- +dnl +dnl Configurator for chkpath +dnl +dnl (c) 1999 Mark Wooding +dnl + +dnl----- Licensing notice --------------------------------------------------- +dnl +dnl This file is part of chkpath. +dnl +dnl chkpath is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl chkpath is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with chkpath; if not, write to the Free Software Foundation, +dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +mdw_AUTO_VERSION +AC_INIT([Checkpath], AUTO_VERSION, [mdw@distorted.org.uk]) +AC_CONFIG_SRCDIR([checkpath.c]) +AC_CONFIG_AUX_DIR([config]) +mdw_SILENT_RULES +AM_INIT_AUTOMAKE([foreign]) + +dnl-------------------------------------------------------------------------- +dnl C language environemnt. + +dnl Compiler and tools. +AC_PROG_CC +AX_CFLAGS_WARN_ALL +AM_PROG_LIBTOOL +mdw_LIBTOOL_VERSION_INFO +AC_SUBST([AM_CFLAGS]) + +dnl Types. +AC_TYPE_UID_T + +dnl Packages. +PKG_CHECK_MODULES([mLib], [mLib >= 2.0.4]) +AM_CFLAGS="$AM_CFLAGS $mLib_CFLAGS" + +dnl-------------------------------------------------------------------------- +dnl Output. + +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_HEADER([config/config.h]) + +AC_OUTPUT + +dnl----- That's all, folks -------------------------------------------------- diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000..643ce57 --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,14 @@ +files +tmp +substvars +*.substvars +*.debhelper +libcheckpath +libcheckpath-dev +checkpath +checkpath1 +libcheckpath1 +tmpdir +chkpath +stamp-* +q diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..fc2cad0 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,64 @@ +checkpath (1.2.99~) experimental; urgency=medium + + * (placeholder for next minor version) + + -- Mark Wooding Sun, 07 Jul 2024 22:54:06 +0100 + +checkpath (1.2.5) experimental; urgency=medium + + * tmpdir: Fix segfault resulting from user-controlled format-string. + Not obviously a security problem because arguments and environment + should be trustworthy. + * tmpdir: Report less unhelpful message if the candidate directory lacks + owner permissions. + + -- Mark Wooding Sun, 07 Jul 2024 22:07:35 +0100 + +checkpath (1.2.4.1) experimental; urgency=medium + + * Fix packaging. (No code changes.) + + -- Mark Wooding Mon, 24 Dec 2018 11:51:47 +0000 + +checkpath (1.2.4) experimental; urgency=low + + * tmpdir: Better diagnostics. + * tmpdir: Make `-C DIR' option actually work. + + -- Mark Wooding Tue, 31 Mar 2015 11:58:29 +0100 + +checkpath (1.2.3) experimental; urgency=low + + * libcheckpath-dev: Include static library. + * Build system overhaul. + * Minor code cleanups. + + -- Mark Wooding Tue, 15 Jul 2014 23:36:28 +0100 + +checkpath (1.2.2) experimental; urgency=low + + * Portability fixes. + + -- Mark Wooding Tue, 06 Dec 2011 10:35:49 +0000 + +checkpath (1.2.1) experimental; urgency=low + + * Include useful information in pkg-config script. + * Introduce -g option to chkpath. + + -- Mark Wooding Sun, 13 Apr 2008 22:02:21 +0100 + +checkpath (1.2.0) experimental; urgency=low + + * Build system overhaul. + * tmpdir: Only use effective uid to choose directory names. + * tmpdir: Option for verbose reporting. + * tmpdir: Option to trust particular groups (e.g., group 0). + + -- Mark Wooding Sun, 13 Apr 2008 17:38:51 +0100 + +checkpath (1.1.0) experimental; urgency=low + + * Debianization! + + -- Mark Wooding Sun, 9 Nov 2003 17:21:10 +0000 diff --git a/debian/chkpath.install b/debian/chkpath.install new file mode 100644 index 0000000..ea25bb7 --- /dev/null +++ b/debian/chkpath.install @@ -0,0 +1,2 @@ +debian/tmp/usr/bin/chkpath +debian/tmp/usr/share/man/man1/chkpath.1 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..45971a2 --- /dev/null +++ b/debian/control @@ -0,0 +1,54 @@ +Source: checkpath +Section: admin +Priority: extra +Build-Depends: + debhelper (>= 8), + pkg-config, + mlib-dev (>= 2.0.4) +Maintainer: Mark Wooding +Standards-Version: 3.1.1 + +Package: checkpath +Architecture: all +Depends: chkpath, tmpdir +Description: Convenience package for the checkpath binaries. + +Package: tmpdir +Architecture: any +Depends: ${shlibs:Depends} +Description: Create a secure temporary directory. + tmpdir creates a temporary directory which can't be subverted by other + users. Nobody except you and root will be able to read or write to your + temporary directory, or make you refer to files outside of it by messing + with parent directories. + +Package: chkpath +Architecture: any +Depends: ${shlibs:Depends} +Description: Checks PATH variable for security + chkpath warns about other users who can write to directories on your PATH, + or rename them, or whatever. If checkpath gives you a clean bill of health + then nobody but you and root can make command names mean different things + without direct write access to the program files. + +Package: libcheckpath1 +Architecture: any +Section: libs +Depends: ${shlibs:Depends} +Description: Checks paths for security + Walks a pathname, checking every symlink and directory on the way, and + issuing alerts if any element is writable by someone other than the calling + uid or root. Useful if you're paranoid. This package just contains the + shared library. If you want to write programs which use this library, you + need libcheckpath-dev. + +Package: libcheckpath-dev +Architecture: any +Section: devel +Depends: libcheckpath1 (= ${binary:Version}), mlib-dev (>= 2.0.0), libc6-dev +Description: Checks paths for security + Walks a pathname, checking every symlink and directory on the way, and + issuing alerts if any element is writable by someone other than the calling + uid or root. Useful if you're paranoid. This package contains the header + files and static libraries you need to compile programs which use the + library. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..ab4efee --- /dev/null +++ b/debian/copyright @@ -0,0 +1,16 @@ +checkpath is copyright (c) 2003 Straylight/Edgeware. + +checkpath is free software; you can redistribute it and/or modify +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. + +checkpath 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 a copy of the GNU General Public License in +/usr/share/common-licenses/GPL; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. diff --git a/debian/libcheckpath-dev.install b/debian/libcheckpath-dev.install new file mode 100644 index 0000000..11e56e4 --- /dev/null +++ b/debian/libcheckpath-dev.install @@ -0,0 +1,6 @@ +debian/tmp/usr/include +debian/tmp/usr/share/man/man3 +debian/tmp/usr/lib/libcheckpath.a +debian/tmp/usr/lib/libcheckpath.la +debian/tmp/usr/lib/pkgconfig +debian/tmp/usr/lib/libcheckpath.so diff --git a/debian/libcheckpath1.install b/debian/libcheckpath1.install new file mode 100644 index 0000000..182f140 --- /dev/null +++ b/debian/libcheckpath1.install @@ -0,0 +1 @@ +debian/tmp/usr/lib/libcheckpath.so.* diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..8817962 --- /dev/null +++ b/debian/rules @@ -0,0 +1,2 @@ +#! /usr/bin/make -f +%:; dh $@ --builddirectory=debian/build diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..d3827e7 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +1.0 diff --git a/debian/tmpdir.install b/debian/tmpdir.install new file mode 100644 index 0000000..be1ebff --- /dev/null +++ b/debian/tmpdir.install @@ -0,0 +1,2 @@ +debian/tmp/usr/bin/tmpdir +debian/tmp/usr/share/man/man1/tmpdir.1 diff --git a/tmpdir.1 b/tmpdir.1 new file mode 100644 index 0000000..05030ba --- /dev/null +++ b/tmpdir.1 @@ -0,0 +1,97 @@ +.\" -*-nroff-*- +.TH tmpdir 1 "6 April 1999" "Local tools" +.SH NAME +tmpdir \- choose, or check a choice of, temporary directory +.SH SYNOPSIS +.B tmpdir +.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 +.B TMPDIR +variable suitable for execution by a shell. +.PP +Many programs aren't sufficiently careful about how they handle +temporary files. For example, if a program which creates files in +.B /tmp +without making careful checks beforehand, a malicious user who can +predict the name that the program will use can create a symbolic link +with that name: when run, the program will then overwrite some file +using your current privileges. Similarly, many programs create +temporary files using generous default permissions, which may well be a +mistake. +.PP +The +.B tmpdir +program finds a secure place for temporary files, creating one if +necessary. The criteria it uses to choose a place are as follows: +.IP " 1." +The temporary directory must be owned by the user, and have mode 700 +(i.e., readable, writable and searchable only by the owner). +.IP " 2." +The path through the filesystem to the temporary directory must be +secure against modifications by other malicious users. See the +.BR chkpath (1) +manual page for a description of how this is done: the two programs work +in the same way. +.PP +First, +.B tmpdir +checks to see whether the current value of the +.B TMPDIR +environment variable is a secure place for temporary files. If so, it +is accepted immediately. Otherwise, it tries to find or create a +directory in +.B /tmp +(on the assumption that this is a fast disk suitable for temporary +files), with the name +.BI /tmp/ user \- suffix +for some +.IR suffix . +If that fails, it tries to create a directory in your home directory, +with the name +.BI ~/tmp\- suffix\fR. +If +.I that +fails too, then +.B tmpdir +gives up: if your home directory's not secure (or full) than a secure +temporary directory is the least of your worries. +.SS Options +The following options are supported: +.TP +.B "\-b, \-\-bourne" +Output an assignment using Bourne shell syntax. The default is to +examine the user's shell and decide which syntax to use based on that. +.TP +.B "\-c, \-\-cshell" +Output an assignment using C shell syntax. +.TP +.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 +isn't). +.SH BUGS +None known. +.SH SEE ALSO +.BR chkpath (1), +.BR checkpath (3), +.BR tmpnam (3), +.BR tmpfile (3). +.SH AUTHOR +Mark Wooding (mdw@nsict.org). diff --git a/tmpdir.c b/tmpdir.c new file mode 100644 index 0000000..7b6fc64 --- /dev/null +++ b/tmpdir.c @@ -0,0 +1,418 @@ +/* -*-c-*- + * + * Choose and check temporary directories + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "checkpath.h" +#include "utils.h" + +/*----- Static variables --------------------------------------------------*/ + +static uid_t me; +static struct checkpath cp; +static struct passwd *pw; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @ok@ --- * + * + * Arguments: @const char *p@ = pathname to check + * @int *f@ = try-to-create flag + * + * Returns: Nonzero if the directory is OK + * + * Use: Ensures that a directory is OK. If @f@ is a real pointer, + * and @*f@ is set, then try to create the directory. + */ + +static void complain(const char *p, const char *msg, int err) +{ + dstr d = DSTR_INIT; + + if (!cp.cp_verbose) return; + dstr_putf(&d, "Path: %s: %s", p, msg); + if (err) dstr_putf(&d, ": %s", strerror(err)); + moan("%s", d.buf); + dstr_destroy(&d); +} + +static int ok(const char *p, int *f) +{ + struct stat st; + + /* --- Read the directory status --- */ + + if (lstat(p, &st)) { + + /* --- Maybe create it if it doesn't exist --- */ + + if (errno != ENOENT || !f || !*f) { + complain(p, "can't stat", errno); + return (0); + } + if (mkdir(p, 0700)) { + complain(p, "can't create", errno); + *f = 0; + return (0); + } + + /* --- Now read the new status --- * + * + * This fixes a race condition between the previous @lstat@ call and + * the @mkdir@. + */ + + if (lstat(p, &st)) { + complain(p, "can't stat after creating", errno); + return (0); + } + } + + /* --- Make sure the directory is good --- * + * + * It must be a genuine directory (not a link); it must be readable + * and writable only by its owner, and that owner must be me. + */ + + if (!S_ISDIR(st.st_mode)) + complain(p, "not a directory", 0); + else if (st.st_uid != me) + complain(p, "not owner", 0); + else if (st.st_mode & 0077) + complain(p, "non-owner access permitted", 0); + else if (~st.st_mode & 0700) + complain(p, "owner lacks permissions", 0); + else + return (1); + return (0); +} + +/* --- @trytmp@ --- * + * + * Arguments: @const char *parent@ = parent directory name + * @const char *base@ = subdirectory base name + * + * Returns: Pointer to directory name, or null. (String needs freeing.) + * + * Use: Tries to find or create an appropriate temporary directory. + */ + +static char *trytmp(const char *parent, const char *base) +{ + static char c[] = { "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" }; + char *p, *q; + char *qq; + dstr d = DSTR_INIT; + int createflag = 1; + + /* --- Make sure the parent directory is sane --- * + * + * Must make sure that all the lead-up to the temporary directory is safe. + * Otherwise other people can play with symlinks or rename directories + * after I've done all this careful work to make the endpoint directory + * safe. + */ + + if (checkpath(parent, &cp)) + return (0); + + /* --- See whether the trivial version will work --- */ + + dstr_putf(&d, "%s/%s", parent, base); + if (ok(d.buf, &createflag)) + goto good; + + /* --- Now try with various suffixes --- */ + + DENSURE(&d, 4); + qq = d.buf + d.len; + *qq++ = '-'; + + for (p = c; *p; p++) { + qq[0] = *p; + qq[1] = 0; + if (ok(d.buf, &createflag)) + goto good; + for (q = c; *q; q++) { + qq[1] = *q; + qq[2] = 0; + if (ok(d.buf, &createflag)) + goto good; + } + } + + /* --- No joy --- */ + + dstr_destroy(&d); + return (0); + +good: + p = xstrdup(d.buf); + dstr_destroy(&d); + return (p); +} + +/* --- @fullcheck@ --- * + * + * Arguments: @const char *p@ = pointer to path to check + * + * Returns: Zero if it's a bad directory. + * + * Use: Runs a thorough check on a directory. + */ + +static int fullcheck(const char *p) + { return (checkpath(p, &cp) == 0 && ok(p, 0)); } + +/* --- @goodtmp@ --- * + * + * Arguments: --- + * + * Returns: Pointer to a known-good secure temporary directory, or + * null. + * + * Use: Finds a good place to store temporary files. + */ + +static char *goodtmp(void) +{ + char *p, *q; + + /* --- First of all, try the user's current choice --- */ + + if ((p = getenv("TMPDIR")) != 0 && fullcheck(p)) + return (p); + + /* --- Try making a directory in `/tmp' --- */ + + if ((q = trytmp("/tmp", pw->pw_name)) != 0) + return (q); + + /* --- That failed: try a directory in the user's home --- */ + + if ((q = trytmp(pw->pw_dir, "tmp")) != 0) + return (q); + + /* --- Still no joy: give up --- * + * + * To be fair, if the user can't find a safe place in his own home + * directory then he's pretty stuffed. + */ + + 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) + { fprintf(fp, "Usage: %s [-bcv] [-g NAME] [-C PATH]\n", QUIS); } + +/* --- @version@ --- */ + +static void version(FILE *fp) + { fprintf(fp, "%s version %s\n", QUIS, VERSION); } + +/* --- @help@ --- */ + +static void help(FILE *fp) +{ + version(fp); + putc('\n', fp); + usage(fp); + fputs("\n\ +Sets a suitable and secure temporary directory, or checks that a given\n\ +directory is suitable for use with temporary files. A directory is\n\ +considered good for a particular user if it's readable and writable only\n\ +by that user, and if all its parents are modifiable only by the user or\n\ +root.\n\ +\n\ +Options supported:\n\ +\n\ +-h, --help Display this help text.\n\ +-V, --version Display the program's version number.\n\ +-u, --usage Display a terse usage summary.\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, --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); +} + +/* --- @main@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = the actual argument strings + * + * Returns: Zero if all went well, else nonzero. + * + * Use: Performs helpful operations on temporary directories. + */ + +int main(int argc, char *argv[]) +{ + int shell = 0; + int duff = 0; + char *p; + + enum { + sh_unknown, + sh_bourne, + sh_csh + }; + + /* --- Initialize variables --- */ + + ego(argv[0]); + me = cp.cp_uid = geteuid(); + cp.cp_what = (CP_WRWORLD | CP_WROTHGRP | CP_WROTHUSR | + CP_STICKYOK | CP_REPORT | CP_ERROR); + cp.cp_verbose = 0; + cp.cp_report = report; + cp.cp_gids = 0; /* ignore group membership */ + pw = getpwuid(me); + if (!pw) + die(1, "you don't exist"); + + /* --- Parse arguments --- */ + + for (;;) { + static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "usage", 0, 0, 'u' }, + { "bourne", 0, 0, 'b' }, + { "cshell", 0, 0, 'c' }, + { "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" "bcvtg:C:", opts, 0, 0, 0); + + if (i < 0) + break; + switch (i) { + case 'h': + help(stdout); + exit(0); + case 'V': + version(stdout); + exit(0); + case 'u': + usage(stdout); + exit(0); + case 'b': + shell = sh_bourne; + break; + case 'c': + shell = sh_csh; + break; + case 'C': + return (!fullcheck(optarg)); + break; + case 'g': + allowgroup(&cp, optarg); + break; + case 'v': + cp.cp_verbose++; + break; + default: + duff = 1; + break; + } + } + + if (duff || optind != argc) { + usage(stderr); + exit(1); + } + + /* --- Choose a shell --- */ + + if (!shell) { + if (!(p = getenv("SHELL"))) + p = pw->pw_shell; + if (strstr(p, "csh")) + shell = sh_csh; + else + shell = sh_bourne; + } + + /* --- Start the checking --- */ + + 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); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..6182d08 --- /dev/null +++ b/utils.c @@ -0,0 +1,87 @@ +/* -*-c-*- + * + * Utilities not worth librarifying + * + * (c) 2008 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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 +#include + +#include + +#include + +#include +#include + +#include "checkpath.h" +#include "utils.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @allowgroup@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to structure to mess with + * @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. + */ + +void allowgroup(struct checkpath *cp, 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; +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..fc29aee --- /dev/null +++ b/utils.h @@ -0,0 +1,60 @@ +/* -*-c-*- + * + * Utilities not worth librarifying + * + * (c) 2008 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of chkpath. + * + * chkpath is free software; you can redistribute it and/or modify + * 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. + */ + +#ifndef UTILS_H +#define UTILS_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include "checkpath.h" + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @allowgroup@ --- * + * + * Arguments: @struct checkpath *cp@ = pointer to structure to mess with + * @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. + */ + +extern void allowgroup(struct checkpath */*cp*/, const char */*gname*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif