mirror of
https://github.com/rd235/cado
synced 2024-12-27 06:24:11 +01:00
209 lines
5.7 KiB
C
209 lines
5.7 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <libgen.h>
|
|
#include <limits.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <pam_check.h>
|
|
#include <get_user_groups.h>
|
|
#include <capset_from_namelist.h>
|
|
#include <read_conf.h>
|
|
#include <set_ambient_cap.h>
|
|
#include <cado_scado_check.h>
|
|
|
|
/* 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);
|
|
}
|