Пример #1
0
static int get_syscall_nr(const char *name) {
    int result = seccomp_syscall_resolve_name(name);
    if (result == __NR_SCMP_ERROR) {
        errx(EXIT_FAILURE, "non-existent syscall: %s", name);
    }
    return result;
}
Пример #2
0
int main(int argc, char *argv[])
{
	char *name = NULL;

	if (seccomp_syscall_resolve_name("open") != __NR_open)
		goto fail;
	if (seccomp_syscall_resolve_name("socket") != __NR_socket)
		goto fail;
	if (seccomp_syscall_resolve_name("INVALID") != __NR_SCMP_ERROR)
		goto fail;

	if (seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE,
					      "open") != __NR_open)
		goto fail;
	if (seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE,
					      "socket") != __NR_socket)
		goto fail;
	if (seccomp_syscall_resolve_name_arch(SCMP_ARCH_NATIVE,
					      "INVALID") != __NR_SCMP_ERROR)
		goto fail;

	name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, __NR_open);
	if (name == NULL || strcmp(name, "open") != 0)
		goto fail;
	free(name);

	name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, __NR_socket);
	if (name == NULL || strcmp(name, "socket") != 0)
		goto fail;
	free(name);

	name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE,
						__NR_SCMP_ERROR);
	if (name != NULL)
		goto fail;
	free(name);

	return 0;

fail:
	if (name != NULL)
		free(name);
	return 1;
}
Пример #3
0
bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx,
			uint32_t action)
{
	int nr, ret;

	ret = seccomp_arch_exist(ctx, arch);
	if (arch && ret != 0) {
		ERROR("BUG: Seccomp: rule and context arch do not match (arch "
		      "%d): %s.",
		      arch, strerror(-ret));
		return false;
	}

	if (strncmp(line, "reject_force_umount", 19) == 0) {
		INFO("Setting Seccomp rule to reject force umounts.");
		ret = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(umount2),
				1, SCMP_A1(SCMP_CMP_MASKED_EQ , MNT_FORCE , MNT_FORCE ));
		if (ret < 0) {
			ERROR("Failed (%d) loading rule to reject force "
			      "umount: %s.",
			      ret, strerror(-ret));
			return false;
		}
		return true;
	}

	nr = seccomp_syscall_resolve_name(line);
	if (nr == __NR_SCMP_ERROR) {
		WARN("Seccomp: failed to resolve syscall: %s.", line);
		WARN("This syscall will NOT be blacklisted.");
		return true;
	}
	if (nr < 0) {
		WARN("Seccomp: got negative for syscall: %d: %s.", nr, line);
		WARN("This syscall will NOT be blacklisted.");
		return true;
	}
	ret = seccomp_rule_add_exact(ctx, action, nr, 0);
	if (ret < 0) {
		ERROR("Failed (%d) loading rule for %s (nr %d action %d): %s.",
		      ret, line, nr, action, strerror(-ret));
		return false;
	}
	return true;
}
int main(int argc, char *argv[])
{
  const char *argv0 = argv[0];
  int opt, optidx;

  bool separate_read_write = false;

  bool proxy = false;
  struct sockaddr_in proxy_sin;

  bool join = false;

#ifdef HAVE_SECCOMP
  int nrules = 0;
  uint32_t def_action = SCMP_ACT_ALLOW;
  char *fixed = (char *)mmap((void *)0x800000, 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
#endif

  struct option longopts[] = {
    {"abstract"    , no_argument       , 0 , 'a'} ,
    {"directory"   , required_argument , 0 , 'd'} ,
    {"environment" , required_argument , 0 , 'e'} ,
    {"join"        , no_argument       , 0 , 'j'} ,
    {"policy"      , required_argument , 0 , 'o'} ,
    {"proxy"       , required_argument , 0 , 'p'} ,
    {"seccomp"     , required_argument , 0 , 'c'} ,
    {"separate"    , no_argument       , 0 , 's'} ,
    {0             , 0                 , 0 , 0}   ,
  };
  while ((opt = getopt_long(argc, argv, "+0ac:d:e:hjo:p:s", longopts, &optidx)) != -1) {
    switch (opt) {
    case 'a':
      af_unix = true;
      break;
    case 'c': {
#ifdef HAVE_SECCOMP
      char *pp = optarg, *p, *qq, *q, *saved0, *saved1;
      for (; ; pp = NULL) {
        p = strtok_r(pp, " \t,", &saved0);
        if (! p) break;

        bool negative = false;
        int num, pos, nfilters = 0;
        enum scmp_compare op;
        if (nrules >= SIZE(scmp_rules))
          errx(BAD_ARG, "too many rules");
        if (*p == '+')
          p++;
        else if (*p == '-')
          p++, negative = true;
        scmp_rules[nrules].action = negative ? SCMP_ACT_TRAP : SCMP_ACT_ALLOW;

        for (qq = p; ; qq = NULL) {
          q = strtok_r(qq, ":", &saved1);
          if (! q) break;
          if (qq) {
            num = seccomp_syscall_resolve_name(q);
            if (num < 0)
              errx(BAD_ARG, "unknown syscall \"%s\"", q);
            scmp_rules[nrules].syscall = num;
          } else {
            if (nfilters >= SIZE(scmp_rules[0].arg_array))
              errx(BAD_ARG, "too many filters");
            if (! ('0' <= *q && *q <= '5'))
              errx(BAD_ARG, "argument position should be 0-5");
            pos = *q-'0';
            REP(i, nfilters)
              if (pos == scmp_rules[nrules].arg_array[i].arg)
                errx(BAD_ARG, "duplicate argument position %d", pos);
            q++;
            if (*q == '=')
              q++, op = SCMP_CMP_EQ;
            else if (*q == '!' && q[1] == '=')
              q += 2, op = SCMP_CMP_NE;
            else if (*q == '<') {
              if (q[1] == '=')
                q += 2, op = SCMP_CMP_LE;
              else
                q++, op = SCMP_CMP_LT;
            } else if (*q == '>') {
              if (q[1] == '=')
                q += 2, op = SCMP_CMP_GE;
              else
                q++, op = SCMP_CMP_GT;
            }
            else
              errx(BAD_ARG, "unknown operator \"%c\"", *q);
            scmp_datum_t val = strtol(q, &q, 0);
            if (*q)
              errx(BAD_ARG, "invalid number");
            scmp_rules[nrules].arg_array[nfilters++] = SCMP_CMP(pos, op, val);
          }
        }
        scmp_rules[nrules].arg_cnt = nfilters;
        nrules++;
      }
#else
      errx(ERR_SYSCALL, "HAVE_SECCOMP not enabled");
#endif
      break;
    }
    case 'd':
      Chdir(optarg);
      break;
    case 'h':
      show_help(STDOUT_FILENO, argv0);
      break;
    case 'j':
      join = true;
      break;
    case 'e':
      putenv(optarg);
      break;
    case 'o':
#ifdef HAVE_SECCOMP
      if (optarg[0] == 'k')
        def_action = SCMP_ACT_KILL;
      else if (optarg[0] == 't')
        def_action = SCMP_ACT_TRAP;
      else if (optarg[0] == 'a')
        def_action = SCMP_ACT_ALLOW;
#else
      errx(ERR_SYSCALL, "HAVE_SECCOMP not enabled");
#endif
      break;
    case 'p': // proxy
      {
        char *p = strchr(optarg, ':');
        if (! p)
          errx(BAD_ARG, "no semicolon");
        *p = '\0';
        proxy_sin.sin_family = AF_INET;
        if (inet_aton(optarg, &proxy_sin.sin_addr) < 0)
          errx(BAD_ARG, "gethostbyname: %s", strerror(errno));
        proxy_sin.sin_port = htons(strtol(p+1, &p, 10));
        if (*p)
          errx(BAD_ARG, "port");
        proxy = true;
      }
      break;
    case 's':
      separate_read_write = true;
      break;
    default:
      show_help(STDERR_FILENO, argv0);
      break;
    }
  }

  argc -= optind;
  argv += optind;

  if (argc < 1) {
    show_help(STDERR_FILENO, argv0);
    return BAD_ARG;
  }

  char buf[4096];
  int pipe_p2c[2], pipe_c2p1[2], pipe_c2p2[2];
  if (af_unix) {
    unix_p2c.sun_family = AF_UNIX; 
    unix_c2p1.sun_family = AF_UNIX;
    unix_c2p2.sun_family = AF_UNIX;
    unix_p2c.sun_path[0] = '\0';
    unix_c2p1.sun_path[0] = '\0';
    unix_c2p2.sun_path[0] = '\0';
    sprintf(unix_p2c.sun_path+1, "/tmp/unix/%d-%d.in", getuid(), getpid());
    sprintf(unix_c2p1.sun_path+1, "/tmp/unix/%d-%d.out", getuid(), getpid());
    sprintf(unix_c2p2.sun_path+1, "/tmp/unix/%d-%d.err", getuid(), getpid());
    pipe_p2c[0] = socket(AF_UNIX, SOCK_STREAM, 0);
    pipe_c2p1[1] = socket(AF_UNIX, SOCK_STREAM, 0);
    if (! join)
      pipe_c2p2[1] = socket(AF_UNIX, SOCK_STREAM, 0);
    atexit(atexit_rm);
    if (bind(pipe_p2c[0], &unix_p2c, sizeof unix_p2c) < 0 ||
        bind(pipe_c2p1[1], &unix_c2p1, sizeof unix_c2p1) < 0)
      return perror(""), 0;
    if (! join && bind(pipe_c2p2[1], &unix_c2p2, sizeof unix_c2p2) < 0)
      return perror(""), 0;
    if (listen(pipe_p2c[0], 1) < 0 ||
        listen(pipe_c2p1[1], 1) < 0)
      return perror(""), 0;
    if (! join && listen(pipe_c2p2[1], 1) < 0)
      return perror(""), 0;
  } else {
    Pipe(pipe_p2c);
    Pipe(pipe_c2p1);
    if (! join)
      Pipe(pipe_c2p2);
  }

  child = Fork();
  if (! child) {
    // child
    if (af_unix) {
      // close bound sockets
      close(pipe_p2c[0]);
      close(pipe_c2p1[1]);
      close(pipe_c2p2[1]);

      // domain sockets for client
      pipe_p2c[1] = socket(AF_UNIX, SOCK_STREAM, 0);
      pipe_c2p1[0] = socket(AF_UNIX, SOCK_STREAM, 0);
      if (! join)
        pipe_c2p2[0] = socket(AF_UNIX, SOCK_STREAM, 0);

      if (connect(pipe_p2c[1], &unix_p2c, sizeof unix_p2c) < 0 ||
          connect(pipe_c2p1[0], &unix_c2p1, sizeof unix_c2p1) < 0)
        return 0;
      dup2(pipe_p2c[1], STDIN_FILENO); close(pipe_p2c[1]);
      dup2(pipe_c2p1[0], STDOUT_FILENO); close(pipe_c2p1[0]);
      if (join)
        dup2(STDOUT_FILENO, STDERR_FILENO);
      else {
        connect(pipe_c2p2[0], &unix_c2p2, sizeof unix_c2p2);
        dup2(pipe_c2p2[0], STDERR_FILENO); close(pipe_c2p2[0]);
      }
    } else {
      close(pipe_p2c[1]); dup2(pipe_p2c[0], STDIN_FILENO); close(pipe_p2c[0]);
      close(pipe_c2p1[0]); dup2(pipe_c2p1[1], STDOUT_FILENO); close(pipe_c2p1[1]);
      if (join)
        dup2(STDOUT_FILENO, STDERR_FILENO);
      else {
        close(pipe_c2p2[0]); dup2(pipe_c2p2[1], STDERR_FILENO); close(pipe_c2p2[1]);
      }
    }

#ifdef HAVE_SECCOMP
    scmp_filter_ctx ctx = seccomp_init(def_action);
    if (ctx == NULL)
      return 1;
    REP(i, nrules) {
      int ret = seccomp_rule_add_array(ctx, scmp_rules[i].action, scmp_rules[i].syscall,
                                       scmp_rules[i].arg_cnt, scmp_rules[i].arg_array);
      if (ret < 0)
        return 1;
    }
    if (seccomp_load(ctx) < 0)
      return 1;
    seccomp_release(ctx);

    strcpy(fixed, *argv);
    execv(fixed, argv); // execve will change the first argument
#else
    execvp(*argv, argv);
#endif
  } else {
Пример #5
0
static int parse_line(char *line, struct seccomp_args *sargs)
{
	// strtok_r needs a pointer to keep track of where it is in the
	// string.
	char *buf_saveptr;

	// Initialize our struct
	sargs->length = 0;
	sargs->syscall_nr = -1;

	if (strlen(line) == 0)
		return PARSE_ERROR;

	// Initialize tokenizer and obtain first token.
	char *buf_token = strtok_r(line, " \t", &buf_saveptr);
	if (buf_token == NULL)
		return PARSE_ERROR;

	// syscall not available on this arch/kernel
	sargs->syscall_nr = seccomp_syscall_resolve_name(buf_token);
	if (sargs->syscall_nr == __NR_SCMP_ERROR)
		return PARSE_INVALID_SYSCALL;

	// Parse for syscall arguments. Since we haven't yet searched for the
	// next token, buf_token is still the syscall itself so start 'pos' as
	// -1 and only if there is an arg to parse, increment it.
	int pos = -1;
	while (pos < SC_ARGS_MAXLENGTH) {
		buf_token = strtok_r(NULL, " \t", &buf_saveptr);
		if (buf_token == NULL)
			break;
		// we found a token, so increment position and process it
		pos++;
		if (strcmp(buf_token, "-") == 0)	// skip arg
			continue;

		enum scmp_compare op = -1;
		scmp_datum_t value = 0;
		if (strlen(buf_token) == 0) {
			return PARSE_ERROR;
		} else if (strlen(buf_token) == 1) {
			// syscall N (length of '1' indicates a single digit)
			op = SCMP_CMP_EQ;
			value = read_number(buf_token);
		} else if (strncmp(buf_token, ">=", 2) == 0) {
			// syscall >=N
			op = SCMP_CMP_GE;
			value = read_number(&buf_token[2]);
		} else if (strncmp(buf_token, "<=", 2) == 0) {
			// syscall <=N
			op = SCMP_CMP_LE;
			value = read_number(&buf_token[2]);
		} else if (strncmp(buf_token, "!", 1) == 0) {
			// syscall !N
			op = SCMP_CMP_NE;
			value = read_number(&buf_token[1]);
		} else if (strncmp(buf_token, ">", 1) == 0) {
			// syscall >N
			op = SCMP_CMP_GT;
			value = read_number(&buf_token[1]);
		} else if (strncmp(buf_token, "<", 1) == 0) {
			// syscall <N
			op = SCMP_CMP_LT;
			value = read_number(&buf_token[1]);
		} else {
			// syscall NNN
			op = SCMP_CMP_EQ;
			value = read_number(buf_token);
		}
		if (errno != 0)
			return PARSE_ERROR;

		sargs->arg_cmp[sargs->length] = SCMP_CMP(pos, op, value);
		sargs->length++;

		//printf("\nDEBUG: SCMP_CMP(%d, %d, %llu)\n", pos, op, value);
	}
	// too many args
	if (pos >= SC_ARGS_MAXLENGTH)
		return PARSE_ERROR;

	return PARSE_OK;
}
void pcm_json_rule_to_seccomp(json_t *rule)
{
	json_t *syscall, *action, *restrictions;
	int rc;
	int syscall_num;
	int arg_count;
	int i;
	int default_action;
	struct scmp_arg_cmp arg_cmp[ARG_COUNT_MAX];
		
		
	syscall = json_object_get(rule, "syscall");
	if(! json_is_string(syscall)) {
		errx(EXIT_FAILURE, "Expected string for system call");
	}

	action = json_object_get(rule, "action");
	if(! json_is_string(action)) {
		errx(EXIT_FAILURE, "Expected string for action value");
	}

	default_action = pcm_string_to_policy(json_string_value(action));
	if(default_action == -1) {
		errx(EXIT_FAILURE, "Rule action invalid (expecting ALLOW, KILL, or ERRNO)");
	}	

	syscall_num = seccomp_syscall_resolve_name(json_string_value(syscall));
	if(syscall_num == __NR_SCMP_ERROR) {
		errx(EXIT_FAILURE, "System call number is negative?");
	}

	arg_count = 0;
	restrictions = json_object_get(rule, "restrictions");

	if(restrictions) {
		if(! json_is_array(restrictions)) {
			errx(EXIT_FAILURE, "restrictions is not an array");
		}

		if(json_array_size(restrictions) > ARG_COUNT_MAX) {
			errx(EXIT_FAILURE, "too many restrictions :/");
		}

		for(i = 0; i < json_array_size(restrictions); i++) {
			json_t *arg_restriction;
			arg_restriction = json_array_get(restrictions, i);
			if(! arg_restriction) {
				errx(EXIT_FAILURE, "fetching entry from array failed?");
			}

			if(! json_is_object(arg_restriction)) {
				errx(EXIT_FAILURE, "argument restriction is not an object");
			}

			pcm_json_restriction_to_arg_cmp(arg_restriction, &arg_cmp[i]);
		}
		arg_count = i;
	}

	rc = seccomp_rule_add_array(PCM_GLOBAL.seccomp, default_action, syscall_num, arg_count, arg_cmp);
	if(rc < 0) {
		errx(EXIT_FAILURE, "Adding rule failed?");
	}


}
Пример #7
0
int main(int argc, char* argv[], char* envp[]) {
    
    if (argc<3 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "--version")) {
        fprintf(stderr, "Usage: limit_syscalls syscall1 syscall2 ... syscallN -- program [arguments]\n");
        fprintf(stderr, "Example:\n"
            "   limit_syscalls execve exit write read open close mmap2  fstat64 access  mprotect set_thread_area -- /bin/echo qqq\n");
        fprintf(stderr, "Advanced:\n");
        fprintf(stderr, "   LIMIT_SYSCALLS_DEFAULT_ACTION={a,k,eN} - by default allow, kill or return error N\n");
        fprintf(stderr, "   some_syscall,A0>=3,A4<<1000,A1!=4,A2&&0x0F==0x0F - compare arguments\n");
        fprintf(stderr, "   some_syscall,{a,k,eN} - allow, kill or return error N for this rule\n");
        fprintf(stderr, "Some more example:\n");
        fprintf(stderr,"    LIMIT_SYSCALLS_DEFAULT_ACTION=a  limit_syscalls  'write,A0==1,e0' -- /usr/bin/printf --help\n");
        fprintf(stderr,"     (this makes write to stdout in /usr/bin/printf silently fail, looping it)\n");
        return 126;
    }
    
    // ECHRNG, just to provide more or less noticable message when we block a syscall
    uint32_t default_action = SCMP_ACT_ERRNO(44);
    
    if (getenv("LIMIT_SYSCALLS_DEFAULT_ACTION")) {
        const char* e = getenv("LIMIT_SYSCALLS_DEFAULT_ACTION");
        if(!strcmp(e, "a")) default_action=SCMP_ACT_ALLOW;
        else 
        if(!strcmp(e, "k")) default_action=SCMP_ACT_KILL;
        else
        if(e[0] == 'e') {
            int errno_;
            if (sscanf(e+1,"%i", &errno_)!=1) {
                fprintf(stderr, "LIMIT_SYSCALLS_DEFAULT_ACTION=e<number> expected\n");
                return 109;
            }
            default_action=SCMP_ACT_ERRNO(errno_);
        }
        else
        {
            fprintf(stderr, "LIMIT_SYSCALLS_DEFAULT_ACTION should be a, k or e<number>\n");
            return 110;
        } 
    }
    
    scmp_filter_ctx ctx = seccomp_init(default_action);
    if (!ctx) {
        perror("seccomp_init");
        return 126;
    }
    
    int i;
    
    for (i=1; i<argc; ++i) {
        if (!strcmp(argv[i], "--")) break;
            
        const char* syscall_name = strtok(argv[i], ",");
                
        int syscall = seccomp_syscall_resolve_name(argv[i]);
        
        if (syscall == __NR_SCMP_ERROR) {
            fprintf(stderr, "Failed to resolve syscall %s\n", syscall_name);
            return 125;
        }
        
        int nargs = 0;
        struct scmp_arg_cmp args[6];
        
        uint32_t action = SCMP_ACT_ALLOW;
        
        const char* aa = strtok(NULL, ",");
        for (;aa; aa=strtok(NULL, ",")) {
            if (aa[0]=='A') {
                if(nargs==6) {
                    fprintf(stderr, "Maximum comparator count (6) exceed in %s\n", argv[i]);
                    return 103;
                }
                if( !(aa[1]>='0' && aa[1]<='5') ) {
                    fprintf(stderr, "A[0-5] expected in %s\n", argv[i]);
                    return 100;
                }
                int cmp = 0; /* invalid value */
                
                if(!strncmp(aa+2, "!=", 2)) cmp=SCMP_CMP_NE;
                if(!strncmp(aa+2, "<<", 2)) cmp=SCMP_CMP_LT;
                if(!strncmp(aa+2, "<=", 2)) cmp=SCMP_CMP_LE;
                if(!strncmp(aa+2, "==", 2)) cmp=SCMP_CMP_EQ;
                if(!strncmp(aa+2, ">=", 2)) cmp=SCMP_CMP_GE;
                if(!strncmp(aa+2, ">>", 2)) cmp=SCMP_CMP_GT;
                if(!strncmp(aa+2, "&&", 2)) cmp=SCMP_CMP_MASKED_EQ;
                    
                if (!cmp) {
                    fprintf(stderr, "After An there should be comparison operator like"
                        " != << <= == => >> ot &&; in %s\n", argv[i]);
                    return 101;
                }
                
                if (cmp != SCMP_CMP_MASKED_EQ) {
                    scmp_datum_t datum;
                    if(sscanf(aa+4, "%lli", &datum)!=1) {
                        fprintf(stderr, "After AxOP there should be some sort of number in %s\n", argv[i]);
                        return 102;
                    }
                    
                    args[nargs++] = SCMP_CMP(aa[1]-'0', cmp, datum);
                } else {
                    scmp_datum_t mask;
                    scmp_datum_t datum;
                    if(sscanf(aa+4, "%lli==%lli", &mask, &datum)!=2) {
                        fprintf(stderr, "After Ax&& there should be number==number; in %s\n", argv[i]);
                        return 104;
                    }
                    
                    args[nargs++] = SCMP_CMP(aa[1]-'0', SCMP_CMP_MASKED_EQ, mask, datum);
                }
            } else
            if (aa[0]=='e') {
                int errno_;
                if (sscanf(aa+1,"%i", &errno_)!=1) {
                    fprintf(stderr, "After e should be number in %s\n", argv[i]);
                    return 105;
                }
                
                action = SCMP_ACT_ERRNO(errno_);
            } else
            if (aa[0]=='k') {
                action = SCMP_ACT_KILL;
            } else
            if (aa[0]=='a') {
                action = SCMP_ACT_ALLOW;
            } else {
                fprintf(stderr, "Unknown %c in %s\n", aa[0], argv[i]);
                return 107;
            }
        }
        
        int ret = seccomp_rule_add_hack(ctx, action, syscall, nargs, args);
        
        if (ret!=0) {
            fprintf(stderr, "seccomp_rule_add returned %d\n", ret);
            return 124;
        }
    }
    
    int ret = seccomp_load(ctx);
    if (ret!=0) {
        fprintf(stderr, "seccomp_load returned %d\n", ret);        
    }
    
    execve(argv[i+1], argv+i+1, envp);
    
    perror("execve");    
    return 123;
}