/* SPDX-License-Identifier: GPL-3.0-only */ /* SPDX-FileCopyrightText: 2026 afiw */ /* * can - query filesystem permissions * Copyright (C) 2026 afiw * * This program 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, exclusively version 3 of the License. * * This program 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 this program. If not, see . */ #include #include #include #include #include #include #include #include #include "action.h" #include "group.h" #include "user.h" enum lookup_mode: char { LOOKUP_MODE_GID = 'G', LOOKUP_MODE_GROUP = 'g', LOOKUP_MODE_UID = 'U', LOOKUP_MODE_USER = 'u' }; [[noreturn]] static void exit_with_usage(int ec) { fprintf(ec == EXIT_SUCCESS ? stdout : stderr, "usage: %s [-GghqU] [--gid] [--group] [--help] [--quiet] [--uid] user action path\n", getprogname()); exit(ec); } int main(int argc, char** argv) { struct option const options[] = { { "gid", no_argument, nullptr, 'G' }, { "group", no_argument, nullptr, 'g' }, { "help", no_argument, nullptr, 'h' }, { "quiet", no_argument, nullptr, 'q' }, { "uid", no_argument, nullptr, 'U' } }; int flag; enum lookup_mode lookup_mode = LOOKUP_MODE_USER; char const* lookup; enum action action; char const* path; struct passwd* passwd; struct group* group; uid_t uid; gid_t gid; char const* errstr; /* for strtonum() */ bool quiet = false; bool result; #ifdef HAVE_PLEDGE if (pledge("stdio rpath getpw", nullptr) == -1) { err(EXIT_FAILURE, "pledge"); } #endif opterr = 0; while ((flag = getopt_long(argc, argv, "GghqU", options, nullptr)) != -1) { switch (flag) { case '?': warnx("unrecognized option: %c", optopt); exit_with_usage(EXIT_FAILURE); case 'G': case 'g': case 'U': lookup_mode = flag; break; case 'h': exit_with_usage(EXIT_SUCCESS); case 'q': quiet = true; break; default: errx(EXIT_FAILURE, "getopt returned %c", flag); } } if (argc != optind + 3) { exit_with_usage(EXIT_FAILURE); } lookup = argv[optind]; if ((action = action_of_cstring(argv[optind + 1])) == -1) { warn("%s", argv[optind + 1]); exit_with_usage(EXIT_FAILURE); } path = argv[optind + 2]; switch (lookup_mode) { case LOOKUP_MODE_GID: gid = (gid_t)strtonum(lookup, 0, (gid_t)-1, &errstr); if (errstr != nullptr) { errx(EXIT_FAILURE, "%s: %s", lookup, errstr); } errno = 0; if ((group = getgrgid(gid)) == nullptr) { if (errno == 0) { errx(EXIT_FAILURE, "%d: No such group", gid); } err(EXIT_FAILURE, "%d", gid); } result = can_group(group, action, path); break; case LOOKUP_MODE_GROUP: errno = 0; if ((group = getgrnam(lookup)) == nullptr) { if (errno == 0) { errx(EXIT_FAILURE, "%s: No such group", lookup); } err(EXIT_FAILURE, "%s", lookup); } result = can_group(group, action, path); break; case LOOKUP_MODE_UID: uid = (uid_t)strtonum(lookup, 0, (uid_t)-1, &errstr); if (errstr != nullptr) { errx(EXIT_FAILURE, "%s: %s", lookup, errstr); } errno = 0; if ((passwd = getpwuid(uid)) == nullptr) { if (errno == 0) { errx(EXIT_FAILURE, "%d: No such user", uid); } err(EXIT_FAILURE, "%d", uid); } result = can_user(passwd, action, path); break; case LOOKUP_MODE_USER: errno = 0; if ((passwd = getpwnam(lookup)) == nullptr) { if (errno == 0) { errx(EXIT_FAILURE, "%s: No such user", lookup); } err(EXIT_FAILURE, "%s", lookup); } result = can_user(passwd, action, path); break; } if (!quiet) { printf(result ? "yes\n" : "no\n"); } return result ? EXIT_SUCCESS : EXIT_FAILURE; }