(root)/
strace-6.5/
src/
secontext.c
       1  /*
       2   * Copyright (c) 2020-2022 The strace developers.
       3   * All rights reserved.
       4   *
       5   * SPDX-License-Identifier: LGPL-2.1-or-later
       6   */
       7  
       8  #include "defs.h"
       9  
      10  #include <stdlib.h>
      11  #include <fcntl.h>
      12  #include <limits.h>
      13  #include <sys/stat.h>
      14  #include <unistd.h>
      15  #include <selinux/selinux.h>
      16  #include <selinux/label.h>
      17  
      18  #include "largefile_wrappers.h"
      19  #include "number_set.h"
      20  #include "secontext.h"
      21  #include "xmalloc.h"
      22  #include "xstring.h"
      23  
      24  /**
      25   * @param secontext Pointer to security context string.
      26   * @param result    Stores pointer to the beginning of the part to print.
      27   * @return          Number of characters of the string to be printed.
      28   */
      29  static size_t
      30  parse_secontext(char *secontext, char **result)
      31  {
      32  	char *end_pos = NULL;
      33  
      34  	if (!is_number_in_set(SECONTEXT_FULL, secontext_set)) {
      35  		/* We're looking for the type wihch is the third field */
      36  		enum { SECONTEXT_TYPE = 2 };
      37  		char *start_pos = secontext;
      38  
      39  		for (unsigned int i = 0; i <= SECONTEXT_TYPE; i++) {
      40  			end_pos = strchr(start_pos, ':');
      41  
      42  			if (i == SECONTEXT_TYPE) {
      43  				secontext = start_pos;
      44  				break;
      45  			}
      46  
      47  			if (!end_pos)
      48  				break;
      49  
      50  			start_pos = end_pos + 1;
      51  		}
      52  	}
      53  
      54  	size_t len = end_pos ? (size_t) (end_pos - secontext)
      55  			     : strlen(secontext);
      56  
      57  	/* Strip terminating \n as these tend to be present sometimes */
      58  	while (len && secontext[len - 1] == '\n')
      59  		len--;
      60  
      61  	return *result = secontext, len;
      62  }
      63  
      64  static int
      65  get_expected_filecontext(const char *path, char **secontext, int mode)
      66  {
      67  	static struct selabel_handle *hdl;
      68  
      69  	if (!hdl) {
      70  		static bool disabled;
      71  		if (disabled)
      72  			return -1;
      73  
      74  		hdl = selabel_open(SELABEL_CTX_FILE, NULL, 0);
      75  		if (!hdl) {
      76  			perror_msg("could not open SELinux database, disabling "
      77  				   "context mismatch checking");
      78  			disabled = true;
      79  			return -1;
      80  		}
      81  	}
      82  
      83  	return selabel_lookup(hdl, secontext, path, mode);
      84  }
      85  
      86  /*
      87   * Retrieves the SELinux context of the given PID (extracted from the tcb).
      88   * Memory must be freed.
      89   * Returns 0 on success, -1 on failure.
      90   */
      91  static int
      92  selinux_getpidcon(struct tcb *tcp, char **secontext)
      93  {
      94  	if (number_set_array_is_empty(secontext_set, 0))
      95  		return -1;
      96  
      97  	int proc_pid = get_proc_pid(tcp->pid);
      98  	if (!proc_pid)
      99  		return -1;
     100  
     101  	return getpidcon(proc_pid, secontext);
     102  }
     103  
     104  /*
     105   * Retrieves the SELinux context of the given pid and descriptor.
     106   * Memory must be freed.
     107   * Returns 0 on success, -1 on failure.
     108   */
     109  static int
     110  selinux_getfdcon(pid_t pid, int fd, char **secontext, char **expected)
     111  {
     112  	if (number_set_array_is_empty(secontext_set, 0) || pid <= 0 || fd < 0)
     113  		return -1;
     114  
     115  	int proc_pid = get_proc_pid(pid);
     116  	if (!proc_pid)
     117  		return -1;
     118  
     119  	char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
     120  	xsprintf(linkpath, "/proc/%u/fd/%u", proc_pid, fd);
     121  
     122  	int rc = getfilecon(linkpath, secontext);
     123  	if (rc < 0 || !is_number_in_set(SECONTEXT_MISMATCH, secontext_set))
     124  		return rc;
     125  
     126  	/*
     127  	 * We need to resolve the path, because selabel_lookup() doesn't
     128  	 * resolve anything.
     129  	 */
     130  	char buf[PATH_MAX + 1];
     131  	ssize_t n = get_proc_pid_fd_path(proc_pid, fd, buf, sizeof(buf), NULL);
     132  	if ((size_t) n >= (sizeof(buf) - 1))
     133  		return 0;
     134  
     135  	/*
     136  	 * We retrieve stat() here since the path the procfs link resolves into
     137  	 * may be reused by a different file with different context.
     138  	 */
     139  	strace_stat_t st;
     140  	if (stat_file(linkpath, &st))
     141  		return 0;
     142  
     143  	get_expected_filecontext(buf, expected, st.st_mode);
     144  
     145  	return 0;
     146  }
     147  
     148  /*
     149   * Retrieves the SELinux context of the given path.
     150   * Memory must be freed.
     151   * Returns 0 on success, -1 on failure.
     152   */
     153  static int
     154  selinux_getfilecon(struct tcb *tcp, const char *path, char **secontext,
     155  		   char **expected)
     156  {
     157  	if (number_set_array_is_empty(secontext_set, 0))
     158  		return -1;
     159  
     160  	int proc_pid = get_proc_pid(tcp->pid);
     161  	if (!proc_pid)
     162  		return -1;
     163  
     164  	int rc = -1;
     165  	char fname[PATH_MAX];
     166  
     167  	if (path[0] == '/')
     168  		rc = snprintf(fname, sizeof(fname), "/proc/%u/root%s",
     169  			       proc_pid, path);
     170  	else if (tcp->last_dirfd == AT_FDCWD)
     171  		rc = snprintf(fname, sizeof(fname), "/proc/%u/cwd/%s",
     172  			       proc_pid, path);
     173  	else if (tcp->last_dirfd >= 0 )
     174  		rc = snprintf(fname, sizeof(fname), "/proc/%u/fd/%u/%s",
     175  			       proc_pid, tcp->last_dirfd, path);
     176  
     177  	if ((unsigned int) rc >= sizeof(fname))
     178  		return -1;
     179  
     180  	rc = getfilecon(fname, secontext);
     181  	if (rc < 0 || !is_number_in_set(SECONTEXT_MISMATCH, secontext_set))
     182  		return rc;
     183  
     184  	/*
     185  	 * We need to fully resolve the path, because selabel_lookup() doesn't
     186  	 * resolve anything.  Using realpath() is the only solution here to make
     187  	 * sure the path is canonicalized.
     188  	 */
     189  
     190  	char *resolved = realpath(fname, NULL);
     191  	if (!resolved)
     192  		return 0;
     193  
     194  	strace_stat_t st;
     195  	if (stat_file(resolved, &st) < 0)
     196  		goto out;
     197  
     198  	get_expected_filecontext(resolved, expected, st.st_mode);
     199  
     200  out:
     201  	free(resolved);
     202  
     203  	return 0;
     204  }
     205  
     206  static void
     207  print_context(char *secontext, char *expected)
     208  {
     209  	if (!secontext)
     210  		return;
     211  
     212  	unsigned int style = QUOTE_OMIT_LEADING_TRAILING_QUOTES
     213  			     | QUOTE_OVERWRITE_HEXSTR |
     214  			     (xflag == HEXSTR_NONE
     215  			      ? QUOTE_HEXSTR_NONE
     216  			      : QUOTE_HEXSTR_NON_ASCII_CHARS);
     217  
     218  	char *ctx_str;
     219  	ssize_t ctx_len = parse_secontext(secontext, &ctx_str);
     220  
     221  	print_quoted_string_ex(ctx_str, ctx_len, style, "[]!");
     222  
     223  	if (!expected)
     224  		goto freecon_secontext;
     225  
     226  	char *exp_str;
     227  	ssize_t exp_len = parse_secontext(expected, &exp_str);
     228  
     229  	if (ctx_len != exp_len || strncmp(ctx_str, exp_str, ctx_len)) {
     230  		tprints_string("!!");
     231  		print_quoted_string_ex(exp_str, exp_len, style, "[]!");
     232  	}
     233  
     234  	freecon(expected);
     235  freecon_secontext:
     236  	freecon(secontext);
     237  }
     238  
     239  void
     240  selinux_printfdcon(pid_t pid, int fd)
     241  {
     242  	char *ctx = NULL;
     243  	char *exp = NULL;
     244  
     245  	if (selinux_getfdcon(pid, fd, &ctx, &exp) < 0)
     246  		return;
     247  
     248  	tprint_space();
     249  	tprint_attribute_begin();
     250  	print_context(ctx, exp);
     251  	tprint_attribute_end();
     252  }
     253  
     254  void
     255  selinux_printfilecon(struct tcb *tcp, const char *path)
     256  {
     257  	char *ctx = NULL;
     258  	char *exp = NULL;
     259  
     260  	if (selinux_getfilecon(tcp, path, &ctx, &exp) < 0)
     261  		return;
     262  
     263  	tprint_space();
     264  	tprint_attribute_begin();
     265  	print_context(ctx, exp);
     266  	tprint_attribute_end();
     267  }
     268  
     269  void
     270  selinux_printpidcon(struct tcb *tcp)
     271  {
     272  	char *ctx = NULL;
     273  
     274  	if (selinux_getpidcon(tcp, &ctx) < 0)
     275  		return;
     276  
     277  	tprint_attribute_begin();
     278  	print_context(ctx, NULL);
     279  	tprint_attribute_end();
     280  	tprint_space();
     281  }