can/can.c

134 lines
4.2 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only */
/* SPDX-FileCopyrightText: 2026 afiw <afiw@linuxposting.xyz> */
/*
* can - query filesystem permissions
* Copyright (C) 2026 afiw <afiw@linuxposting.xyz>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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] 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; }