From d7b5ee0cc2a612023bb20492a75af4a7a23e856b Mon Sep 17 00:00:00 2001 From: mdw Date: Sat, 25 Jan 2003 23:58:44 +0000 Subject: [PATCH] Make guts into official library. --- .links | 1 + Makefile.am | 20 ++- checkpath.3 | 172 ++++++++++++++++++++ checkpath.c | 67 ++++---- checkpath.h | 45 ++--- chkpath.1 | 3 +- chkpath.c | 20 ++- configure.in | 10 +- path.c | 523 ----------------------------------------------------------- path.h | 115 ------------- setup | 1 + tmpdir.1 | 1 + tmpdir.c | 15 +- 13 files changed, 284 insertions(+), 709 deletions(-) create mode 100644 checkpath.3 delete mode 100644 path.c delete mode 100644 path.h diff --git a/.links b/.links index bf86bc5..3225c08 100644 --- a/.links +++ b/.links @@ -2,3 +2,4 @@ COPYING missing mkinstalldirs install-sh +lib-config.in diff --git a/Makefile.am b/Makefile.am index 349cd6e..b08ae56 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## -*-makefile-*- ## -## $Id: Makefile.am,v 1.3 1999/11/11 19:10:52 mdw Exp $ +## $Id: Makefile.am,v 1.4 2003/01/25 23:58:44 mdw Exp $ ## ## Makefile for chkpath ## @@ -28,6 +28,9 @@ ##----- Revision history ---------------------------------------------------- ## ## $Log: Makefile.am,v $ +## Revision 1.4 2003/01/25 23:58:44 mdw +## Make guts into official library. +## ## Revision 1.3 1999/11/11 19:10:52 mdw ## Build separately from mLib. ## @@ -40,12 +43,21 @@ AUTOMAKE_OPTIONS = foreign +lib_LTLIBRARIES = libcheckpath.la +libcheckpath_la_LDFLAGS = -version-info 2:0:1 +## Middle number is the patchlevel. Final number is the minor version. The +## difference between the first and last numbers is the major version. +libcheckpath_la_SOURCES = checkpath.c +include_HEADERS = checkpath.h + +bin_SCRIPTS = checkpath-config bin_PROGRAMS = chkpath tmpdir -man_MANS = chkpath.1 tmpdir.1 +man_MANS = chkpath.1 tmpdir.1 checkpath.3 EXTRA_DIST = $(man_MANS) -chkpath_SOURCES = chkpath.c path.c path.h -tmpdir_SOURCES = tmpdir.c path.c path.h +chkpath_SOURCES = chkpath.c +tmpdir_SOURCES = tmpdir.c +LDADD = libcheckpath.la ##----- That's all, folks --------------------------------------------------- diff --git a/checkpath.3 b/checkpath.3 new file mode 100644 index 0000000..8bc8faf --- /dev/null +++ b/checkpath.3 @@ -0,0 +1,172 @@ +.\" -*-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 "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. +.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 index 28ca474..993cce2 100644 --- a/checkpath.c +++ b/checkpath.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: checkpath.c,v 1.4 2001/01/25 22:16:02 mdw Exp $ + * $Id: checkpath.c,v 1.5 2003/01/25 23:58:44 mdw Exp $ * * Check a path for safety * @@ -29,6 +29,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: checkpath.c,v $ + * Revision 1.5 2003/01/25 23:58:44 mdw + * Make guts into official library. + * * Revision 1.4 2001/01/25 22:16:02 mdw * Make flags be unsigned. * @@ -61,7 +64,7 @@ #include #include -#include "path.h" +#include "checkpath.h" /*----- Data structures ---------------------------------------------------*/ @@ -194,8 +197,8 @@ static void push(struct elt *e) /* --- @report@ --- * * - * Arguments: @struct chkpath *cp@ = pointer to context - * @int what@ = what sort of report is this? + * 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 @@ -205,7 +208,7 @@ static void push(struct elt *e) * Use: Formats and presents messages to the client. */ -static void report(struct chkpath *cp, int what, int verbose, +static void report(const struct checkpath *cp, unsigned what, int verbose, const char *p, const char *msg, ...) { /* --- Decide whether to bin this message --- */ @@ -283,7 +286,7 @@ static void report(struct chkpath *cp, int what, int verbose, * * Arguments: @const char *p@ = name of directory to check * @struct stat *st@ = pointer to @stat@(2) block for it - * @struct chkpath *cp@ = pointer to caller parameters + * @const struct checkpath *cp@ = pointer to caller parameters * @unsigned f@ = various flags * * Returns: Zero if everything's OK, else bitmask of problems. @@ -291,16 +294,20 @@ static void report(struct chkpath *cp, int what, int verbose, * Use: Performs the main load of sanity-checking on a directory. */ -static int sanity(const char *p, struct stat *st, - struct chkpath *cp, unsigned f) +static unsigned sanity(const char *p, struct stat *st, + const struct checkpath *cp, unsigned f) { - int bad = 0; - int sticky = (cp->cp_what & CP_STICKYOK) || !(f & f_last) ? 01000 : 0; + unsigned bad = 0; + int stickyok = 0; + + 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 | sticky)) == 0002) { + (st->st_mode & (0002 | stickyok)) == 0002) { bad |= CP_WRWORLD; report(cp, CP_WRWORLD, 1, p, "** world writable **"); } @@ -308,19 +315,21 @@ static int sanity(const char *p, struct stat *st, /* --- Check for group-writability --- */ if ((cp->cp_what & (CP_WRGRP | CP_WROTHGRP)) && - (st->st_mode & (0020 | sticky)) == 0020) { - if (cp->cp_what & CP_WRGRP) { - bad |= CP_WRGRP; - report(cp, CP_WRGRP, 1, p, "writable by group %g", st->st_gid); - } else { - int i; + (st->st_mode & (0020 | stickyok)) == 0020) { + int i; + unsigned 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]) - goto good_gid; + b = cp->cp_what & CP_WRGRP; } - bad |= CP_WROTHGRP; - report(cp, CP_WROTHGRP, 1, p, "writable by group %g", st->st_gid); - good_gid:; + } + if (b) { + bad |= b; + report(cp, b, 1, p, "writable by %sgroup %g", + (b == CP_WROTHGRP) ? "other " : "", st->st_gid); } } @@ -338,10 +347,10 @@ static int sanity(const char *p, struct stat *st, return (bad); } -/* --- @path_check@ --- * +/* --- @checkpath@ --- * * * Arguments: @const char *p@ = directory name which needs checking - * @struct chkpath *cp@ = caller parameters for the check + * @const struct checkpath *cp@ = parameters for the check * * Returns: Zero if all is well, otherwise bitmask of problems. * @@ -349,12 +358,12 @@ static int sanity(const char *p, struct stat *st, * users could do to it. */ -int path_check(const char *p, struct chkpath *cp) +unsigned checkpath(const char *p, const struct checkpath *cp) { char cwd[PATH_MAX]; struct elt *e, *ee; struct stat st; - int bad = 0; + unsigned bad = 0; /* --- Initialize stack pointer and path string --- */ @@ -494,16 +503,16 @@ int path_check(const char *p, struct chkpath *cp) return (bad); } -/* --- @path_setids@ --- * +/* --- @checkpath_setids@ --- * * - * Arguments: @struct chkpath *cp@ = pointer to block to fill in + * Arguments: @struct checkpath *cp@ = pointer to block to fill in * - * Returns: Zero if OK, else @-1@. + * Returns: --- * * Use: Fills in the user ids and things in the structure. */ -void path_setids(struct chkpath *cp) +void checkpath_setids(struct checkpath *cp) { int n, i; gid_t g = getgid(); diff --git a/checkpath.h b/checkpath.h index 74cd0f5..726f9f8 100644 --- a/checkpath.h +++ b/checkpath.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: checkpath.h,v 1.2 2001/01/25 22:16:02 mdw Exp $ + * $Id: checkpath.h,v 1.3 2003/01/25 23:58:44 mdw Exp $ * * Check a path for safety * @@ -29,6 +29,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: checkpath.h,v $ + * Revision 1.3 2003/01/25 23:58:44 mdw + * Make guts into official library. + * * Revision 1.2 2001/01/25 22:16:02 mdw * Make flags be unsigned. * @@ -37,8 +40,8 @@ * */ -#ifndef PATH_H -#define PATH_H +#ifndef CHECKPATH_H +#define CHECKPATH_H #ifdef __cplusplus extern "C" { @@ -57,13 +60,13 @@ * looked for, and what to do when they're found. */ -struct chkpath { +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)(int /*what*/, int /*verb*/, + void (*cp_report)(unsigned /*what*/, int /*verb*/, const char */*dir*/, const char */*msg*/, void */*p*/); void *cp_arg; /* Argument for cp_report */ @@ -71,21 +74,25 @@ struct chkpath { /* --- Flags for `@what@' fields in the above --- */ -#define CP_ERROR 1u /* Error report */ -#define CP_WRWORLD 2u /* Check write by world */ -#define CP_WRGRP 4u /* Check write by any group */ -#define CP_WROTHGRP 8u /* Check write by other group */ -#define CP_WROTHUSR 16u /* Check write by other user */ -#define CP_SYMLINK 32u /* Report symbolic links */ -#define CP_REPORT 64u /* Make user-readable reports */ -#define CP_STICKYOK 128u /* Don't care if sticky is set */ +/* 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 ------------------------------------------------*/ -/* --- @path_check@ --- * +/* --- @checkpath@ --- * * * Arguments: @const char *p@ = directory name which needs checking - * @struct chkpath *cp@ = caller parameters for the check + * @const struct checkpath *cp@ = parameters for the check * * Returns: Zero if all is well, otherwise bitmask of problems. * @@ -93,18 +100,18 @@ struct chkpath { * users could do to it. */ -extern int path_check(const char */*p*/, struct chkpath */*cp*/); +extern unsigned checkpath(const char */*p*/, const struct checkpath */*cp*/); -/* --- @path_setids@ --- * +/* --- @checkpath_setids@ --- * * - * Arguments: @struct chkpath *cp@ = pointer to block to fill in + * Arguments: @struct checkpath *cp@ = pointer to block to fill in * * Returns: --- * * Use: Fills in the user ids and things in the structure. */ -extern void path_setids(struct chkpath */*cp*/); +extern void checkpath_setids(struct checkpath */*cp*/); /*----- That's all, folks -------------------------------------------------*/ diff --git a/chkpath.1 b/chkpath.1 index 4689f5f..f8ce180 100644 --- a/chkpath.1 +++ b/chkpath.1 @@ -106,6 +106,7 @@ PATH=`chkpath -qqp` .SH BUGS None known. .SH SEE ALSO -.BR tmpdir (1). +.BR tmpdir (1), +.BR checkpath (3). .SH AUTHOR Mark Wooding (mdw@nsict.org). diff --git a/chkpath.c b/chkpath.c index 250e922..cf86c7f 100644 --- a/chkpath.c +++ b/chkpath.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: chkpath.c,v 1.2 2001/01/25 22:16:02 mdw Exp $ + * $Id: chkpath.c,v 1.3 2003/01/25 23:58:44 mdw Exp $ * * Check a user's file search path * @@ -29,6 +29,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: chkpath.c,v $ + * Revision 1.3 2003/01/25 23:58:44 mdw + * Make guts into official library. + * * Revision 1.2 2001/01/25 22:16:02 mdw * Make flags be unsigned. * @@ -50,11 +53,11 @@ #include #include -#include "path.h" +#include "checkpath.h" /*----- Main code ---------------------------------------------------------*/ -static void report(int what, int verbose, +static void report(unsigned what, int verbose, const char *p, const char *msg, void *arg) { @@ -106,10 +109,10 @@ Options provided are:\n\ int main(int argc, char *argv[]) { - int bad = 0; + unsigned bad = 0; int i; char *p, *q, *path; - struct chkpath cp; + struct checkpath cp; int f = 0; #define f_print 1u @@ -122,11 +125,10 @@ int main(int argc, char *argv[]) /* --- Set up path scanning defaults --- */ cp.cp_verbose = 1; - cp.cp_what = (CP_WRWORLD | CP_WRGRP | CP_WROTHUSR | - CP_ERROR | CP_REPORT | CP_SYMLINK); + cp.cp_what = CP_PROBLEMS | CP_REPORT | CP_SYMLINK; cp.cp_report = report; cp.cp_arg = 0; - path_setids(&cp); + checkpath_setids(&cp); /* --- Parse the options --- */ @@ -196,7 +198,7 @@ int main(int argc, char *argv[]) p = xstrdup(argv[i]); q = strtok(p, ":"); while (q) { - int b = path_check(q, &cp); + unsigned b = checkpath(q, &cp); if (!b && (f & f_print)) { if (f & f_colon) putchar(':'); diff --git a/configure.in b/configure.in index 34be244..ad741fc 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ dnl -*-fundamental-*- dnl -dnl $Id: configure.in,v 1.2 1999/11/11 19:10:52 mdw Exp $ +dnl $Id: configure.in,v 1.3 2003/01/25 23:58:44 mdw Exp $ dnl dnl Configurator for chkpath dnl @@ -28,6 +28,9 @@ dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. dnl ----- Revision history -------------------------------------------------- dnl dnl $Log: configure.in,v $ +dnl Revision 1.3 2003/01/25 23:58:44 mdw +dnl Make guts into official library. +dnl dnl Revision 1.2 1999/11/11 19:10:52 mdw dnl Build separately from mLib. dnl @@ -36,10 +39,11 @@ dnl Import new project. dnl AC_INIT(chkpath.c) -AM_INIT_AUTOMAKE(chkpath, 1.0.1) +mdw_INIT_LIB(checkpath, checkpath, 1.1.0, chkpath) AC_PROG_CC mdw_GCC_FLAGS +AM_PROG_LIBTOOL mdw_MLIB(1.6.0) -AC_OUTPUT(Makefile) +AC_OUTPUT(Makefile checkpath-config:lib-config.in) dnl ----- That's all, folks ------------------------------------------------- diff --git a/path.c b/path.c deleted file mode 100644 index f731e52..0000000 --- a/path.c +++ /dev/null @@ -1,523 +0,0 @@ -/* -*-c-*- - * - * $Id: path.c,v 1.4 2001/01/25 22:16:02 mdw Exp $ - * - * 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. - */ - -/*----- Revision history --------------------------------------------------* - * - * $Log: path.c,v $ - * Revision 1.4 2001/01/25 22:16:02 mdw - * Make flags be unsigned. - * - * Revision 1.3 1999/05/21 22:07:20 mdw - * Take advantage of new dynamic string macros. - * - * Revision 1.2 1999/05/18 20:49:12 mdw - * Use a dynamic string for reading symlinks. - * - * Revision 1.1.1.1 1999/04/06 20:12:07 mdw - * Import new project. - * - */ - -/*----- Header files ------------------------------------------------------*/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#include "path.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: @struct chkpath *cp@ = pointer to context - * @int 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(struct chkpath *cp, int what, int verbose, - const char *p, const char *msg, ...) -{ - /* --- Decide whether to bin this message --- */ - - if (!cp->cp_report || verbose > cp->cp_verbose || !(cp->cp_what & what)) - 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; - } - } - 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); -} - -/* --- @sanity@ --- * - * - * Arguments: @const char *p@ = name of directory to check - * @struct stat *st@ = pointer to @stat@(2) block for it - * @struct chkpath *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 int sanity(const char *p, struct stat *st, - struct chkpath *cp, unsigned f) -{ - int bad = 0; - int sticky = (cp->cp_what & CP_STICKYOK) || !(f & f_last) ? 01000 : 0; - - /* --- Check for world-writability --- */ - - if ((cp->cp_what & CP_WRWORLD) && - (st->st_mode & (0002 | sticky)) == 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 | sticky)) == 0020) { - if (cp->cp_what & CP_WRGRP) { - bad |= CP_WRGRP; - report(cp, CP_WRGRP, 1, p, "writable by group %g", st->st_gid); - } else { - int i; - for (i = 0; i < cp->cp_gids; i++) { - if (st->st_gid == cp->cp_gid[i]) - goto good_gid; - } - bad |= CP_WROTHGRP; - report(cp, CP_WROTHGRP, 1, p, "writable by group %g", st->st_gid); - good_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); -} - -/* --- @path_check@ --- * - * - * Arguments: @const char *p@ = directory name which needs checking - * @struct chkpath *cp@ = caller 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. - */ - -int path_check(const char *p, struct chkpath *cp) -{ - char cwd[PATH_MAX]; - struct elt *e, *ee; - struct stat st; - int 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); -} - -/* --- @path_setids@ --- * - * - * Arguments: @struct chkpath *cp@ = pointer to block to fill in - * - * Returns: Zero if OK, else @-1@. - * - * Use: Fills in the user ids and things in the structure. - */ - -void path_setids(struct chkpath *cp) -{ - int n, i; - gid_t g = getgid(); - - 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; - } - cp->cp_gid[n++] = g; -gid_ok: - cp->cp_gids = n; -} - -/*----- That's all, folks -------------------------------------------------*/ diff --git a/path.h b/path.h deleted file mode 100644 index fc7478a..0000000 --- a/path.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*-c-*- - * - * $Id: path.h,v 1.2 2001/01/25 22:16:02 mdw Exp $ - * - * 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. - */ - -/*----- Revision history --------------------------------------------------* - * - * $Log: path.h,v $ - * Revision 1.2 2001/01/25 22:16:02 mdw - * Make flags be unsigned. - * - * Revision 1.1.1.1 1999/04/06 20:12:07 mdw - * Import new project. - * - */ - -#ifndef PATH_H -#define PATH_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 chkpath { - 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)(int /*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 --- */ - -#define CP_ERROR 1u /* Error report */ -#define CP_WRWORLD 2u /* Check write by world */ -#define CP_WRGRP 4u /* Check write by any group */ -#define CP_WROTHGRP 8u /* Check write by other group */ -#define CP_WROTHUSR 16u /* Check write by other user */ -#define CP_SYMLINK 32u /* Report symbolic links */ -#define CP_REPORT 64u /* Make user-readable reports */ -#define CP_STICKYOK 128u /* Don't care if sticky is set */ - -/*----- Functions provided ------------------------------------------------*/ - -/* --- @path_check@ --- * - * - * Arguments: @const char *p@ = directory name which needs checking - * @struct chkpath *cp@ = caller 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 int path_check(const char */*p*/, struct chkpath */*cp*/); - -/* --- @path_setids@ --- * - * - * Arguments: @struct chkpath *cp@ = pointer to block to fill in - * - * Returns: --- - * - * Use: Fills in the user ids and things in the structure. - */ - -extern void path_setids(struct chkpath */*cp*/); - -/*----- That's all, folks -------------------------------------------------*/ - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/setup b/setup index 27dde72..5d05592 100755 --- a/setup +++ b/setup @@ -3,6 +3,7 @@ set -e mklinks mkaclocal +libtoolize autoconf automake mkdir build diff --git a/tmpdir.1 b/tmpdir.1 index 38eb10b..c6286d8 100644 --- a/tmpdir.1 +++ b/tmpdir.1 @@ -80,6 +80,7 @@ isn't). None known. .SH SEE ALSO .BR chkpath (1), +.BR checkpath (3), .BR tmpnam (3), .BR tmpfile (3). .SH AUTHOR diff --git a/tmpdir.c b/tmpdir.c index fed0810..943c4d4 100644 --- a/tmpdir.c +++ b/tmpdir.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: tmpdir.c,v 1.3 1999/05/21 22:07:20 mdw Exp $ + * $Id: tmpdir.c,v 1.4 2003/01/25 23:58:44 mdw Exp $ * * Choose and check temporary directories * @@ -29,6 +29,9 @@ /*----- Revision history --------------------------------------------------* * * $Log: tmpdir.c,v $ + * Revision 1.4 2003/01/25 23:58:44 mdw + * Make guts into official library. + * * Revision 1.3 1999/05/21 22:07:20 mdw * Take advantage of new dynamic string macros. * @@ -58,12 +61,12 @@ #include #include -#include "path.h" +#include "checkpath.h" /*----- Static variables --------------------------------------------------*/ static uid_t me; -static struct chkpath cp; +static struct checkpath cp; static struct passwd *pw; /*----- Main code ---------------------------------------------------------*/ @@ -145,7 +148,7 @@ static char *trytmp(const char *parent, const char *base) * safe. */ - if (path_check(parent, &cp)) + if (checkpath(parent, &cp)) return (0); /* --- See whether the trivial version will work --- */ @@ -195,7 +198,7 @@ good: static int fullcheck(const char *p) { - return (path_check(p, &cp) == 0 && ok(p, 0)); + return (checkpath(p, &cp) == 0 && ok(p, 0)); } /* --- @goodtmp@ --- * @@ -312,7 +315,7 @@ int main(int argc, char *argv[]) cp.cp_what = CP_WRWORLD | CP_WRGRP | CP_WROTHUSR | CP_STICKYOK; cp.cp_verbose = 0; cp.cp_report = 0; - path_setids(&cp); + checkpath_setids(&cp); pw = getpwuid(me); if (!pw) die(1, "you don't exist"); -- 2.11.0