/* --- @usage@ --- */
static void usage(FILE *fp)
- { fprintf(fp, "Usage: %s [-pqstv] [-g NAME] [PATH...]\n", QUIS); }
+ { fprintf(fp, "Usage: %s [-Tpqstv] [-g NAME] [PATH...]\n", QUIS); }
/* --- @version@ --- */
-V, --version Display the program's version number.\n\
-u, --usage Show a terse usage summary.\n\
\n\
+-T, --private-group Accept paths writable by primary group if it has\n\
+ no other members.\n\
-g, --group NAME Consider members of group NAME trustworthy.\n\
-p, --print Write the secure path elements to standard output.\n\
-q, --quiet Be quiet about the search progress (cumulative).\n\
int i;
char *p, *q, *path;
struct checkpath cp;
+ gid_t gid;
int f = 0;
#define f_print 1u
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'V' },
{ "usage", 0, 0, 'u' },
+ { "private-group", 0, 0, 'T' },
{ "group", OPTF_ARGREQ, 0, 'g' },
{ "print", 0, 0, 'p' },
{ "quiet", 0, 0, 'q' },
{ 0, 0, 0, 0 }
};
- i = mdwopt(argc, argv, "hVu" "g:pqstv", opts, 0, 0, 0);
+ i = mdwopt(argc, argv, "hVu" "Tg:pqstv", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case 'u':
usage(stdout);
exit(0);
+ case 'T':
+ if (!private_group(&gid, cp.cp_verbose) &&
+ checkpath_addgid(&cp, gid))
+ die(1, "too many groups");
+ break;
case 'g':
allowgroup(&cp, optarg);
break;
is secure, and exit successfully if it is (and unsuccessfully if it
isn't).
.TP
+.B "\-T, \-\-private-group"
+Accept directories which are accessible by the user's primary group
+.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.
/*----- Static variables --------------------------------------------------*/
static uid_t me;
+static gid_t mygroup;
static struct checkpath cp;
static struct passwd *pw;
+static unsigned flags;
+#define F_PRIVGRP 1u
/*----- Main code ---------------------------------------------------------*/
complain(p, "not a directory", 0);
else if (st.st_uid != me)
complain(p, "not owner", 0);
- else if (st.st_mode & (S_IRWXG | S_IRWXO))
- complain(p, "non-owner access permitted", 0);
+ else if (st.st_mode & S_IRWXO)
+ complain(p, "other access permitted", 0);
+ else if (!((flags & F_PRIVGRP) && st.st_gid != mygroup) &&
+ (st.st_mode & S_IRWXG))
+ complain(p, "other-group access permitted", 0);
else if (~st.st_mode & S_IRWXU)
complain(p, "owner lacks permissions", 0);
else
/* --- @usage@ --- */
static void usage(FILE *fp)
- { fprintf(fp, "Usage: %s [-bcv] [-C PATH] [-g NAME]\n", QUIS); }
+ { fprintf(fp, "Usage: %s [-Tbcv] [-C PATH] [-g NAME]\n", QUIS); }
/* --- @version@ --- */
-u, --usage Display a terse usage summary.\n\
\n\
-C, --check PATH Check whether PATH is good, setting exit status.\n\
+-T, --private-group Accept paths writable by primary group if it has\n\
+ no other members.\n\
-b, --bourne Output a `TMPDIR' setting for Bourne shell users.\n\
-c, --cshell Output a `TMPDIR' setting for C shell users.\n\
-g, --group NAME Trust group NAME to be honest and true.\n\
int shell = 0;
int duff = 0;
int i;
+ gid_t gid;
char *p;
enum {
{ "usage", 0, 0, 'u' },
{ "check", OPTF_ARGREQ, 0, 'C' },
{ "verify", OPTF_ARGREQ, 0, 'C' },
+ { "private-group", 0, 0, 'T' },
{ "bourne", 0, 0, 'b' },
{ "cshell", 0, 0, 'c' },
{ "group", OPTF_ARGREQ, 0, 'g' },
{ 0, 0, 0, 0 }
};
- i = mdwopt(argc, argv, "hVu" "C:bcg:v", opts, 0, 0, 0);
+ i = mdwopt(argc, argv, "hVu" "C:Tbcg:qv", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case 'C':
return (!fullcheck(optarg));
break;
+ case 'T':
+ if (!private_group(&gid, cp.cp_verbose)) {
+ mygroup = gid; flags |= F_PRIVGRP;
+ if (checkpath_addgid(&cp, gid))
+ die(1, "too many groups");
+ }
+ break;
case 'b':
shell = sh_bourne;
break;
#include <stdlib.h>
#include <sys/types.h>
+#include <unistd.h>
#include <grp.h>
+#include <pwd.h>
#include <mLib/macros.h>
#include <mLib/report.h>
cp->cp_gid[cp->cp_gids++] = g;
}
+/* --- @private_group@ --- *
+ *
+ * Arguments: @gid_t *gid_out@ = where to put the group id
+ * @int verbose@ = verbosity level
+ *
+ * Returns: Zero on success, %$-1$% if the user's group is not private.
+ *
+ * Use: If the user's primary group has no other configured members,
+ * then set @*gid_out@ to its gid and return zero. Otherwise,
+ * report a message if the verbosity level is sufficiently high,
+ * and return %$-1$%,
+ */
+
+static int check_private_group_members(uid_t uid, struct group *gr,
+ int verbose)
+{
+ struct passwd *pw;
+ char *const *p;
+
+ for (p = gr->gr_mem; *p; p++) {
+ pw = getpwnam(*p);
+ if (!pw) {
+ if (verbose >= 0)
+ moan("can't find `passwd' entry for primary group `%s' member `%s'",
+ gr->gr_name, *p);
+ return (-1);
+ }
+ if (pw->pw_uid != uid) {
+ if (verbose >= 1)
+ moan("primary group `%s' has `%s' (uid %ld) as additional member",
+ gr->gr_name, *p, (long)pw->pw_uid);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int private_group(gid_t *gid_out, int verbose)
+{
+ struct group *gr;
+ struct passwd *pw;
+ uid_t uid = getuid();
+ gid_t gid;
+ int rc;
+ unsigned f = 0;
+#define f_pwiter 1u
+#define f_griter 2u
+
+ /* Look up the user's primary group. */
+ pw = getpwuid(uid);
+ if (!pw) {
+ if (verbose >= 0)
+ moan("can't find `passwd' entry for real uid %ld", (long)uid);
+ rc = -1; goto end;
+ }
+ gid = pw->pw_gid;
+
+ /* Check /all/ groups in case there's another with the same gid which lists
+ * a member other than us.
+ */
+ setgrent(); f |= f_griter;
+ for (;;) {
+ gr = getgrent(); if (!gr) break;
+ if (gr->gr_gid == gid && check_private_group_members(uid, gr, verbose))
+ { rc = -1; goto end; }
+ }
+ endgrent(); f &= ~f_griter;
+
+ /* Check the group members directly. The above check may well not work in
+ * YP environment, for example.
+ */
+ gr = getgrgid(gid);
+ if (!gr) {
+ if (verbose >= 1)
+ moan("can't find `group' entry for real gid %ld", (long)gid);
+ rc = -1; goto end;
+ }
+ if (check_private_group_members(uid, gr, verbose))
+ { rc = -1; goto end; }
+
+ /* Finally, check all other users to see if they list our group as their
+ * primary group.
+ */
+ setpwent(); f |= f_pwiter;
+ for (;;) {
+ pw = getpwent(); if (!pw) break;
+ if (pw->pw_gid == gid && pw->pw_uid != uid) {
+ if (verbose >= 1)
+ moan("user `%s' (uid %ld) shares primary group `%s' (gid %ld)",
+ pw->pw_name, (long)pw->pw_uid,
+ gr->gr_name, (long)gr->gr_gid);
+ rc = -1; goto end;
+ }
+ }
+ endpwent(); f &= ~f_pwiter;
+
+ /* All is well. */
+ if (verbose >= 2)
+ moan("trusting private group `%s' (gid %ld)",
+ gr->gr_name, (long)gr->gr_gid);
+ *gid_out = gid;
+ rc = 0;
+
+end:
+ if (f&f_pwiter) endpwent();
+ if (f&f_griter) endgrent();
+ return (rc);
+
+#undef f_pwiter
+#undef f_griter
+}
+
/*----- That's all, folks -------------------------------------------------*/
extern void allowgroup(struct checkpath */*cp*/, const char */*gname*/);
+/* --- @private_group@ --- *
+ *
+ * Arguments: @gid_t *gid_out@ = where to put the group id
+ * @int verbose@ = verbosity level
+ *
+ * Returns: Zero on success, %$-1$% if the user's group is not private.
+ *
+ * Use: If the user's primary group has no other configured members,
+ * then set @*gid_out@ to its gid and return zero. Otherwise,
+ * report a message if the verbosity level is sufficiently high,
+ * and return %$-1$%,
+ */
+
+extern int private_group(gid_t */*gid_out*/, int /*verbose*/);
+
/*----- That's all, folks -------------------------------------------------*/
#ifdef __cplusplus