/* * cado: execute a command in a capability ambient * Copyright (C) 2016 Renzo Davoli, University of Bologna * * This file is part of cado. * * Cado 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. * * 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 #include #include #include #include #include /* print a capset (in case of -v, verbose mode). */ static void printcapset(uint64_t capset, char *indent) { if (capset) { cap_value_t cap; int count=0; for (cap = 0; cap <= CAP_LAST_CAP; cap++) { if (capset & (1ULL << cap)) { count ++; printf("%s%2d %016" PRIx64 " %s\n", indent, cap, UINT64_C(1) << cap, cap_to_name(cap)); } } if (count > 1) printf("%s %016" PRIx64 "\n", indent, capset); } else printf("%s %016" PRIx64 " NONE\n",indent, UINT64_C(0)); } /* command line args management */ #define OPTSTRING "hqvsS" struct option long_options[]={ {"help", no_argument, NULL, 'h'}, {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, {"setcap", no_argument, NULL, 'v'}, {"scado", no_argument, NULL, 'S'} }; void usage(char *progname) { fprintf(stderr,"%s - execute a command in a different capability ambient\n\n",progname); fprintf(stderr,"usage: %s OPTIONS capability_list command [args]\n\n",progname); fprintf(stderr,"Options:\n"); fprintf(stderr," -h, --help display help message and exit\n"); fprintf(stderr," -q, --quiet do not display warnings, do what it is allowed\n"); fprintf(stderr," -v, --verbose generate extra output\n"); fprintf(stderr," -S, --scado check scado pre-authorization for scripts\n"); fprintf(stderr," -s, --setcap set the minimun caps for %s (root access)\n",progname); exit(1); } int main(int argc, char*argv[]) { char *progname=basename(argv[0]); char **user_groups=get_user_groups(); uint64_t okcaps; uint64_t reqcaps; uint64_t grantcap=0; int verbose=0; int quiet=0; int setcap=0; int scado=0; int pam_check_required = 1; char copy_path[PATH_MAX] = ""; while (1) { int c=getopt_long(argc, argv, OPTSTRING, long_options, NULL); if (c < 0) break; switch (c) { case 'h': usage(progname); break; case 'v': verbose=1; break; case 'q': quiet=1; break; case 's': setcap=1; break; case 'S': scado=1; break; } } /* setcap mode: cado sets the minimal required set of capability required by itself */ if (setcap) { if (setuid(0) != 0 || geteuid() != 0) { fprintf(stderr, "setcap requires root access %d\n",geteuid()); exit(2); } okcaps = get_authorized_caps(NULL, -1LL); okcaps |= 1ULL << CAP_DAC_READ_SEARCH; if (verbose) { printf("Capability needed by %s:\n", progname); printcapset(okcaps, " "); } if (set_self_capability(okcaps) < 0) { fprintf(stderr, "Cannot set %s capabilities\n", progname); exit(2); } exit(0); } if (user_groups == NULL) { fprintf(stderr, "No passwd entry for user '%d'\n",getuid()); exit(2); } /* -v without any other parameter: cado shows the set of ambient capabilities allowed for the current user/group */ if (verbose && (argc == optind)) { okcaps=get_authorized_caps(user_groups, -1LL); printf("Allowed ambient capabilities:\n"); printcapset(okcaps, " "); exit(0); } if (argc - optind < 2) usage(progname); /* parse the set of requested capabilities */ if (capset_from_namelist(argv[optind], &reqcaps)) { fprintf(stderr, "List of capabilities: syntax error\n"); exit(2); } if (verbose) { printf("Requested ambient capabilities:\n"); printcapset(reqcaps, " "); } /* check if the capability requested are also allowed */ okcaps=get_authorized_caps(user_groups, reqcaps); optind++; /* scado mode, check if there is a pre-authorization for the command */ if (scado) { uint64_t scado_caps = cado_scado_check(user_groups[0], argv[optind], copy_path); if (verbose) { printf("Scado permitted capabilities for %s:\n", argv[optind]); printcapset(scado_caps, " "); } okcaps &= scado_caps; pam_check_required = 0; } /* the user requested capabilities which are not allowed */ if (reqcaps & ~okcaps) { if (verbose) { printf("Unavailable ambient capabilities:\n"); printcapset(reqcaps & ~okcaps, " "); } /* if not in "quiet" mode, do not complaint */ if (!quiet) { fprintf(stderr,"%s: Permission denied\n",progname); exit(2); } } grantcap = reqcaps & okcaps; /* revert setgid mode */ setuid(getuid()); /* ask for pam authorization (usually password) if required */ if (pam_check_required && pam_check(user_groups[0]) != PAM_SUCCESS) { fprintf(stderr,"%s: Authentication failure\n",progname); exit(2); } /* okay: grantcap can be granted, do it! */ if (grantcap) set_ambient_cap(grantcap); if (verbose && (reqcaps & ~okcaps)) { printf("Granted ambient capabilities:\n"); printcapset(grantcap, " "); } /* exec the command in the new ambient capability environment */ execvp(copy_path[0] == 0 ? argv[optind] : copy_path, argv+optind); exit(2); }