+/* --- @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
+}
+