Exemplo n.º 1
0
/*
 * Returns 0 ok, 1 deprecated action, 2 rule error,
 * 3 multiple rule insert/delete
 */
static int audit_rule_setup(char *opt, int *filter, int *act, int lineno)
{
	int rc;
	char *p;

	if (++multiple != 1)
		return 3;

	p = strchr(opt, ',');
	if (p == NULL || strchr(p+1, ','))
		return 2;
	*p = 0;

	/* Try opt both ways */
	if (lookup_filter(opt, filter) == 2) {
		rc = lookup_action(opt, act);
		if (rc != 0) {
			*p = ',';
			return rc;
		}
	}

	/* Repair the string */
	*p = ',';
	opt = p+1;

	/* If flags are empty, p+1 must be the filter */
	if (*filter == AUDIT_FILTER_UNSET)
		lookup_filter(opt, filter);
	else {
		rc = lookup_action(opt, act);
		if (rc != 0)
			return rc;
	}

	/* Make sure we set both */
	if (*filter == AUDIT_FILTER_UNSET || *act == -1)
		return 2;

	/* Consolidate rules on exit filter */
	if (*filter == AUDIT_FILTER_ENTRY) {
		*filter = AUDIT_FILTER_EXIT;
		fprintf(stderr,
		    "Warning - entry rules deprecated, changing to exit rule");
		if (lineno)
			fprintf(stderr, " in line %d", lineno);
		fprintf(stderr, "\n");
	}

	return 0;
}
Exemplo n.º 2
0
static int avc_act_parser(struct nv_pair *nv, int line, prelude_conf_t *config)
{
	if (lookup_action(nv->value, &config->avcs_act) == 0)
		return 0;
        syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line);
        return 1;
}
Exemplo n.º 3
0
/* Read the init file.  Return true if no error was encountered.  Set
   SUPPRESS_INFO or SUPPRESS_EA to true if the init file specified to ignore
   default key bindings. */
int
compile (FILE *fp, const char *filename, int *suppress_info, int *suppress_ea)
{
  int error = 0; /* Set if there was a fatal error in reading init file. */
  char rescan = 0; /* Whether to reuse the same character when moving onto the
                      next state. */
  unsigned int lnum = 0;
  int c = 0;

  /* This parser is a true state machine, with no sneaky fetching
     of input characters inside the main loop.  In other words, all
     state is fully represented by the following variables:
   */
  enum
    {
      start_of_line,
      start_of_comment,
      in_line_comment,
      in_trailing_comment,
      get_keyseq,
      got_keyseq,
      get_action,
      got_action,
      get_varname,
      got_varname,
      get_equals,
      got_equals,
      get_value
    }
  state = start_of_line;
  enum sect_e section = info;
  enum
    {
      normal,
      slosh,
      control,
      octal,
      special_key
    }
  seqstate = normal;	/* used if state == get_keyseq */
  char meta = 0;
  char ocnt = 0;	/* used if state == get_keyseq && seqstate == octal */

  /* Data is accumulated in the following variables.  The code
     avoids overflowing these strings, and throws an error
     where appropriate if a string limit is exceeded.  These string
     lengths are arbitrary (and should be large enough) and their
     lengths are not hard-coded anywhere else, so increasing them
     here will not break anything.  */
  char oval = 0;
  char comment[10];
  unsigned int clen = 0;
  int seq[20];
  unsigned int slen = 0;
  char act[80];
  unsigned int alen = 0;
  char varn[80];
  unsigned int varlen = 0;
  char val[80];
  unsigned int vallen = 0;

#define	To_seq(c) \
		  do { \
		    if (slen < sizeof seq/sizeof(int)) \
		      seq[slen++] = meta ? KEYMAP_META(c) : (c); \
		    else \
		      { \
			syntax_error(filename, lnum, \
				     _("key sequence too long")); \
			error = 1; \
		      } \
		    meta = 0; \
		  } while (0)

  while (!error && (rescan || (c = fgetc (fp)) != EOF))
    {
      rescan = 0;
      switch (state)
	{
	case start_of_line:
	  lnum++;
	  if (c == '#')
	    state = start_of_comment;
	  else if (c != '\n')
	    {
	      switch (section)
		{
		case info:
		case ea:
		  state = get_keyseq;
		  seqstate = normal;
		  slen = 0;
		  break;
		case var:
		  state = get_varname;
		  varlen = 0;
		  break;
		}
	      rescan = 1;
	    }
	  break;

	case start_of_comment:
	  clen = 0;
	  state = in_line_comment;
	  /* fall through */
	case in_line_comment:
	  if (c == '\n')
	    {
	      state = start_of_line;
	      comment[clen] = '\0';
	      if (strcmp (comment, "info") == 0)
		section = info;
	      else if (strcmp (comment, "echo-area") == 0)
		section = ea;
	      else if (strcmp (comment, "var") == 0)
		section = var;
	      else if (strcmp (comment, "stop") == 0
		       && (section == info || section == ea))
                {
                  if (section == info)
                    *suppress_info = 1;
                  else
                    *suppress_ea = 1;
                }
	    }
	  else if (clen < sizeof comment - 1)
	    comment[clen++] = c;
	  break;

	case in_trailing_comment:
	  if (c == '\n')
	    state = start_of_line;
	  break;

	case get_keyseq:
	  switch (seqstate)
	    {
	    case normal:
	      if (c == '\n' || isspace (c))
		{
		  state = got_keyseq;
		  rescan = 1;
		  if (slen == 0)
		    {
		      syntax_error (filename, lnum, _("missing key sequence"));
		      error = 1;
		    }
		}
	      else if (c == '\\')
		seqstate = slosh;
	      else if (c == '^')
		seqstate = control;
	      else
		To_seq (c);
	      break;

	    case slosh:
	      switch (c)
		{
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		  seqstate = octal;
		  oval = c - '0';
		  ocnt = 1;
		  break;
		case 'b':
		  To_seq ('\b');
		  seqstate = normal;
		  break;
		case 'e':
		  To_seq ('\033');
		  seqstate = normal;
		  break;
		case 'n':
		  To_seq ('\n');
		  seqstate = normal;
		  break;
		case 'r':
		  To_seq ('\r');
		  seqstate = normal;
		  break;
		case 't':
		  To_seq ('\t');
		  seqstate = normal;
		  break;
		case 'm':
		  meta = 1;
		  seqstate = normal;
		  break;
		case 'k':
		  seqstate = special_key;
		  break;
		default:
		  /* Backslash followed by any other char
		     just means that char.  */
		  To_seq (c);
		  seqstate = normal;
		  break;
		}
	      break;

	    case octal:
	      switch (c)
		{
		case '0': case '1': case '2': case '3':
		case '4': case '5': case '6': case '7':
		  if (++ocnt <= 3)
		    oval = oval * 8 + c - '0';
		  if (ocnt == 3)
		    seqstate = normal;
		  break;
		default:
		  ocnt = 4;
		  seqstate = normal;
		  rescan = 1;
		  break;
		}
	      if (seqstate != octal)
		{
		  if (oval)
		    To_seq (oval);
		  else
		    {
		      syntax_error (filename, lnum,
				    _("NUL character (\\000) not permitted"));
		      error = 1;
		    }
		}
	      break;

	    case special_key:
	      switch (c)
		{
		case 'u': To_seq (KEY_UP_ARROW); break;
		case 'd': To_seq (KEY_DOWN_ARROW); break;
		case 'r': To_seq (KEY_RIGHT_ARROW); break;
		case 'l': To_seq (KEY_LEFT_ARROW); break;
		case 'U': To_seq (KEY_PAGE_UP); break;
		case 'D': To_seq (KEY_PAGE_DOWN); break;
		case 'h': To_seq (KEY_HOME); break;
		case 'e': To_seq (KEY_END); break;
		case 'x': To_seq (KEY_DELETE); break;
		default:  To_seq (c); rescan = 1; break;
		}
	      seqstate = normal;
	      break;

	    case control:
	      if (CONTROL (c))
		To_seq (CONTROL (c));
	      else
		{
		  syntax_error (filename, lnum,
				_("NUL character (^%c) not permitted"), c);
		  error = 1;
		}
	      seqstate = normal;
	      break;
	    }
	  break;

	case got_keyseq:
	  if (isspace (c) && c != '\n')
	    break;
	  state = get_action;
	  alen = 0;
	  /* fall through */
	case get_action:
	  if (c == '\n' || isspace (c))
	    {
	      int a;

	      state = got_action;
	      rescan = 1;
	      if (alen == 0)
		{
		  syntax_error (filename, lnum, _("missing action name"));
		  error = 1;
		}
	      else
		{
		  act[alen] = '\0';
		  a = lookup_action (act);
                  if (a == A_info_menu_digit)
		    {
                      /* It does not make sense for menu-digit to be anything
                         other than '0' .. '9'. */
		      syntax_error (filename, lnum,
                        _("cannot bind key sequence to menu-digit"));
		    }
		  else if (a == -1)
		    {
                      /* Print an error message, but keep going (don't set
                         error = 1) for compatibility with infokey files aimed
                         at future versions which may have different
                         actions. */
		      syntax_error (filename, lnum, _("unknown action `%s'"),
				    act);
		    }
                  else
		    {
                      int keymap_bind_keyseq (Keymap, int *, KEYMAP_ENTRY *);

                      KEYMAP_ENTRY ke;
                      
                      ke.type = ISFUNC;
                      ke.value.function = &function_doc_array[a];
                      To_seq (0);

                      if (section == info)
                        keymap_bind_keyseq (info_keymap, seq, &ke);
                      else /* section == ea */
                        keymap_bind_keyseq (echo_area_keymap, seq, &ke);
		    }
		}
	    }
	  else if (alen < sizeof act - 1)
	    act[alen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("action name too long"));
	      error = 1;
	    }
	  break;

	case got_action:
	  if (c == '#')
	    state = in_trailing_comment;
	  else if (c == '\n')
	    state = start_of_line;
	  else if (!isspace (c))
	    {
	      syntax_error (filename, lnum,
			    _("extra characters following action `%s'"),
			    act);
	      error = 1;
	    }
	  break;

	case get_varname:
	  if (c == '=')
	    {
	      if (varlen == 0)
		{
		  syntax_error (filename, lnum, _("missing variable name"));
		  error = 1;
		}
	      state = get_value;
	      vallen = 0;
	    }
	  else if (c == '\n' || isspace (c))
	    {
	      syntax_error (filename, lnum,
			    _("missing `=' immediately after variable name"));
	      error = 1;
	    }
	  else if (varlen < sizeof varn - 1)
	    varn[varlen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("variable name too long"));
	      error = 1;
	    }
	  break;

	case get_value:
	  if (c == '\n')
	    {
              VARIABLE_ALIST *v;

              state = start_of_line;
              varn[varlen] = '\0';
              val[vallen] = '\0';
              v = variable_by_name (varn);
              if (!v)
                info_error (_("%s: no such variable"), varn);
              else if (!set_variable_to_value (v, val, SET_IN_CONFIG_FILE))
                info_error (_("value %s is not valid for variable %s"),
                              val, varn);
	    }
	  else if (vallen < sizeof val - 1)
	    val[vallen++] = c;
	  else
	    {
	      syntax_error (filename, lnum, _("value too long"));
	      error = 1;
	    }
	  break;

        case get_equals:
        case got_equals:
        case got_varname:
          break;
	}
    }

#undef To_seq

  return !error;
}
/*
 * pipe the callout information to the specified program and retrieve the payload data over another
 * pipe
 */
static void pipe_to_program(char *op,
			    key_serial_t key,
			    char *ktype,
			    char *kdesc,
			    char *callout_info,
			    char *prog,
			    char **argv)
{
	char errbuf[512], payload[32768 + 1], *pp, *pc, *pe;
	int ipi[2], opi[2], epi[2], childpid;
	int ifl, ofl, efl, npay, ninfo, espace, tmp;

	debug("pipe_to_program(%s -> %s)", callout_info, prog);

	if (pipe(ipi) < 0 || pipe(opi) < 0 || pipe(epi) < 0)
		error("pipe failed: %m");

	childpid = fork();
	if (childpid == -1)
		error("fork failed: %m");

	if (childpid == 0) {
		/* child process */
		if (dup2(ipi[0], 0) < 0 ||
		    dup2(opi[1], 1) < 0 ||
		    dup2(epi[1], 2) < 0)
			error("dup2 failed: %m");
		close(ipi[0]);
		close(ipi[1]);
		close(opi[0]);
		close(opi[1]);
		close(epi[0]);
		close(epi[1]);

		execv(prog, argv);
		error("/etc/request-key.conf:%d: Failed to execute '%s': %m\n", confline, prog);
	}

	/* parent process */
	close(ipi[0]);
	close(opi[1]);
	close(epi[1]);

#define TOSTDIN ipi[1]
#define FROMSTDOUT opi[0]
#define FROMSTDERR epi[0]

	ifl = fcntl(TOSTDIN, F_GETFL);
	ofl = fcntl(FROMSTDOUT, F_GETFL);
	efl = fcntl(FROMSTDERR, F_GETFL);
	if (ifl < 0 || ofl < 0 || efl < 0)
		error("fcntl/F_GETFL failed: %m");

	ifl |= O_NONBLOCK;
	ofl |= O_NONBLOCK;
	efl |= O_NONBLOCK;

	if (fcntl(TOSTDIN, F_SETFL, ifl) < 0 ||
	    fcntl(FROMSTDOUT, F_SETFL, ofl) < 0 ||
	    fcntl(FROMSTDERR, F_SETFL, efl) < 0)
		error("fcntl/F_SETFL failed: %m");

	ninfo = strlen(callout_info);
	pc = callout_info;

	npay = sizeof(payload);
	pp = payload;

	espace = sizeof(errbuf);
	pe = errbuf;

	do {
		fd_set rfds, wfds;

		FD_ZERO(&rfds);
		FD_ZERO(&wfds);

		if (TOSTDIN != -1) {
			if (ninfo > 0) {
				FD_SET(TOSTDIN, &wfds);
			}
			else {
				close(TOSTDIN);
				TOSTDIN = -1;
				continue;
			}
		}

		if (FROMSTDOUT != -1)
			FD_SET(FROMSTDOUT, &rfds);

		if (FROMSTDERR != -1)
			FD_SET(FROMSTDERR, &rfds);

		tmp = TOSTDIN > FROMSTDOUT ? TOSTDIN : FROMSTDOUT;
		tmp = tmp > FROMSTDERR ? tmp : FROMSTDERR;
		tmp++;

		debug("select r=%d,%d w=%d m=%d\n", FROMSTDOUT, FROMSTDERR, TOSTDIN, tmp);

		tmp = select(tmp, &rfds, &wfds, NULL, NULL);
		if (tmp < 0)
			error("select failed: %m\n");

		debug("select -> %d r=%x w=%x\n",
		      tmp,
		      *(unsigned *) (void *) &rfds,
		      *(unsigned *) (void *) &wfds);

		if (TOSTDIN != -1 && FD_ISSET(TOSTDIN, &wfds)) {
			tmp = write(TOSTDIN, pc, ninfo);
			if (tmp < 0) {
				if (errno != EPIPE)
					error("write failed: %m\n");

				debug("EPIPE");
				ninfo = 0;
			}
			else {
				debug("wrote %d\n", tmp);

				pc += tmp;
				ninfo -= tmp;
			}
		}

		if (FROMSTDOUT != -1 && FD_ISSET(FROMSTDOUT, &rfds)) {
			tmp = read(FROMSTDOUT, pp, npay);
			if (tmp < 0)
				error("read failed: %m\n");

			debug("read %d\n", tmp);

			if (tmp == 0) {
				close(FROMSTDOUT);
				FROMSTDOUT = -1;
			}
			else {
				pp += tmp;
				npay -= tmp;

				if (npay == 0)
					error("Too much data read from query program\n");
			}
		}

		if (FROMSTDERR != -1 && FD_ISSET(FROMSTDERR, &rfds)) {
			char *nl;

			tmp = read(FROMSTDERR, pe, espace);
			if (tmp < 0)
				error("read failed: %m\n");

			debug("read err %d\n", tmp);

			if (tmp == 0) {
				close(FROMSTDERR);
				FROMSTDERR = -1;
				continue;
			}

			pe += tmp;
			espace -= tmp;

			while ((nl = memchr(errbuf, '\n', pe - errbuf))) {
				int n, rest;

				nl++;
				n = nl - errbuf;

				if (xdebug)
					fprintf(stderr, "Child: %*.*s", n, n, errbuf);

				if (!xnolog) {
					openlog("request-key", 0, LOG_AUTHPRIV);
					syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
					closelog();
				}

				rest = pe - nl;
				if (rest > 0) {
					memmove(errbuf, nl, rest);
					pe -= n;
					espace += n;
				}
				else {
					pe = errbuf;
					espace = sizeof(errbuf);
				}
			}

			if (espace == 0) {
				int n = sizeof(errbuf);

				if (xdebug)
					fprintf(stderr, "Child: %*.*s", n, n, errbuf);

				if (!xnolog) {
					openlog("request-key", 0, LOG_AUTHPRIV);
					syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
					closelog();
				}

				pe = errbuf;
				espace = sizeof(errbuf);
			}
		}

	} while (TOSTDIN != -1 || FROMSTDOUT != -1 || FROMSTDERR != -1);

	/* wait for the program to exit */
	if (waitpid(childpid, &tmp, 0) != childpid)
		error("wait for child failed: %m\n");

	/* if the process exited non-zero or died on a signal, then we call back in to ourself to
	 * decide on negation
	 * - this is not exactly beautiful but the quickest way of having configurable negation
	 *   settings
	 */
	if (WIFEXITED(tmp) && WEXITSTATUS(tmp) != 0) {
		if (norecurse)
			error("child exited %d\n", WEXITSTATUS(tmp));

		norecurse = 1;
		debug("child exited %d\n", WEXITSTATUS(tmp));
		lookup_action("negate", key, ktype, kdesc, callout_info);
	}

	if (WIFSIGNALED(tmp)) {
		if (norecurse)
			error("child died on signal %d\n", WTERMSIG(tmp));

		norecurse = 1;
		debug("child died on signal %d\n", WTERMSIG(tmp));
		lookup_action("negate", key, ktype, kdesc, callout_info);
	}

	/* attempt to instantiate the key */
	debug("instantiate with %zd bytes\n", pp - payload);

	if (keyctl_instantiate(key, payload, pp - payload, 0) < 0)
		error("instantiate key failed: %m\n");

	debug("instantiation successful\n");
	exit(0);

} /* end pipe_to_program() */
int main(int argc, char *argv[])
{
	key_serial_t key;
	char *ktype, *kdesc, *buf, *callout_info;
	int ret, ntype, dpos, n, fd;

	signal(SIGSEGV, oops);
	signal(SIGBUS, oops);
	signal(SIGPIPE, SIG_IGN);

	for (;;) {
		if (argc > 1 && strcmp(argv[1], "-d") == 0) {
			xdebug++;
			argv++;
			argc--;
		}
		else if (argc > 1 && strcmp(argv[1], "-n") == 0) {
			xnolog = 1;
			argv++;
			argc--;
		}
		else
			break;
	}

	if (argc != 8 && argc != 9)
		error("Unexpected argument count: %d\n", argc);

	fd = open("/dev/null", O_RDWR);
	if (fd < 0)
		error("open");
	if (fd > 2) {
		close(fd);
	}
	else if (fd < 2) {
		ret = dup(fd);
		if (ret < 0)
			error("dup failed: %m\n");

		if (ret < 2 && dup(fd) < 0)
			error("dup failed: %m\n");
	}

	xkey = argv[2];
	xuid = argv[3];
	xgid = argv[4];
	xthread_keyring = argv[5];
	xprocess_keyring = argv[6];
	xsession_keyring = argv[7];

	key = atoi(xkey);

	/* assume authority over the key
	 * - older kernel doesn't support this function
	 */
	ret = keyctl_assume_authority(key);
	if (ret < 0 && !(argc == 9 || errno == EOPNOTSUPP))
		error("Failed to assume authority over key %d (%m)\n", key);

	/* ask the kernel to describe the key to us */
	if (xdebug < 2) {
		ret = keyctl_describe_alloc(key, &buf);
		if (ret < 0)
			goto inaccessible;
	}
	else {
		buf = strdup("user;0;0;1f0000;debug:1234");
	}

	/* extract the type and description from the key */
	debug("Key descriptor: \"%s\"\n", buf);
	ntype = -1;
	dpos = -1;

	n = sscanf(buf, "%*[^;]%n;%*d;%*d;%x;%n", &ntype, &n, &dpos);
	if (n != 1)
		error("Failed to parse key description\n");

	ktype = buf;
	ktype[ntype] = 0;
	kdesc = buf + dpos;

	debug("Key type: %s\n", ktype);
	debug("Key desc: %s\n", kdesc);

	/* get hold of the callout info */
	callout_info = argv[8];

	if (!callout_info) {
		void *tmp;

		if (keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, &tmp) < 0)
			error("Failed to retrieve callout info (%m)\n");

		callout_info = tmp;
	}

	debug("CALLOUT: '%s'\n", callout_info);

	/* determine the action to perform */
	lookup_action(argv[1],		/* op */
		      key,		/* ID of key under construction */
		      ktype,		/* key type */
		      kdesc,		/* key description */
		      callout_info	/* call out information */
		      );

inaccessible:
	error("Key %d is inaccessible (%m)\n", key);

} /* end main() */