1 /* runcon -- run command with specified security context
2 Copyright (C) 2005-2023 Free Software Foundation, Inc.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17 /*
18 * runcon [ context
19 * | ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] )
20 * command [arg1 [arg2 ...] ]
21 *
22 * attempt to run the specified command with the specified context.
23 *
24 * -r role : use the current context with the specified role
25 * -t type : use the current context with the specified type
26 * -u user : use the current context with the specified user
27 * -l level : use the current context with the specified level range
28 * -c : compute process transition context before modifying
29 *
30 * Contexts are interpreted as follows:
31 *
32 * Number of MLS
33 * components system?
34 *
35 * 1 - type
36 * 2 - role:type
37 * 3 Y role:type:range
38 * 3 N user:role:type
39 * 4 Y user:role:type:range
40 * 4 N error
41 */
42
43 #include <config.h>
44 #include <stdio.h>
45 #include <getopt.h>
46 #include <selinux/selinux.h>
47 #include <selinux/context.h>
48 #include <sys/types.h>
49 #include "system.h"
50 #include "quote.h"
51
52 /* The official name of this program (e.g., no 'g' prefix). */
53 #define PROGRAM_NAME "runcon"
54
55 #define AUTHORS proper_name ("Russell Coker")
56
57 static struct option const long_options[] =
58 {
59 {"role", required_argument, nullptr, 'r'},
60 {"type", required_argument, nullptr, 't'},
61 {"user", required_argument, nullptr, 'u'},
62 {"range", required_argument, nullptr, 'l'},
63 {"compute", no_argument, nullptr, 'c'},
64 {GETOPT_HELP_OPTION_DECL},
65 {GETOPT_VERSION_OPTION_DECL},
66 {nullptr, 0, nullptr, 0}
67 };
68
69 void
usage(int status)70 usage (int status)
71 {
72 if (status != EXIT_SUCCESS)
73 emit_try_help ();
74 else
75 {
76 printf (_("\
77 Usage: %s CONTEXT COMMAND [args]\n\
78 or: %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\
79 "), program_name, program_name);
80 fputs (_("\
81 Run a program in a different SELinux security context.\n\
82 With neither CONTEXT nor COMMAND, print the current security context.\n\
83 "), stdout);
84
85 emit_mandatory_arg_note ();
86
87 fputs (_("\
88 CONTEXT Complete security context\n\
89 -c, --compute compute process transition context before modifying\n\
90 -t, --type=TYPE type (for same role as parent)\n\
91 -u, --user=USER user identity\n\
92 -r, --role=ROLE role\n\
93 -l, --range=RANGE levelrange\n\
94 "), stdout);
95 fputs (HELP_OPTION_DESCRIPTION, stdout);
96 fputs (VERSION_OPTION_DESCRIPTION, stdout);
97 emit_exec_status (PROGRAM_NAME);
98 emit_ancillary_info (PROGRAM_NAME);
99 }
100 exit (status);
101 }
102
103 int
main(int argc,char ** argv)104 main (int argc, char **argv)
105 {
106 char *role = nullptr;
107 char *range = nullptr;
108 char *user = nullptr;
109 char *type = nullptr;
110 char *context = nullptr;
111 char *cur_context = nullptr;
112 char *file_context = nullptr;
113 char *new_context = nullptr;
114 bool compute_trans = false;
115
116 context_t con;
117
118 initialize_main (&argc, &argv);
119 set_program_name (argv[0]);
120 setlocale (LC_ALL, "");
121 bindtextdomain (PACKAGE, LOCALEDIR);
122 textdomain (PACKAGE);
123
124 initialize_exit_failure (EXIT_CANCELED);
125 atexit (close_stdout);
126
127 while (true)
128 {
129 int option_index = 0;
130 int c = getopt_long (argc, argv, "+r:t:u:l:c", long_options,
131 &option_index);
132 if (c == -1)
133 break;
134 switch (c)
135 {
136 case 'r':
137 if (role)
138 error (EXIT_CANCELED, 0, _("multiple roles"));
139 role = optarg;
140 break;
141 case 't':
142 if (type)
143 error (EXIT_CANCELED, 0, _("multiple types"));
144 type = optarg;
145 break;
146 case 'u':
147 if (user)
148 error (EXIT_CANCELED, 0, _("multiple users"));
149 user = optarg;
150 break;
151 case 'l':
152 if (range)
153 error (EXIT_CANCELED, 0, _("multiple levelranges"));
154 range = optarg;
155 break;
156 case 'c':
157 compute_trans = true;
158 break;
159
160 case_GETOPT_HELP_CHAR;
161 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
162 default:
163 usage (EXIT_CANCELED);
164 break;
165 }
166 }
167
168 if (argc - optind == 0)
169 {
170 if (getcon (&cur_context) < 0)
171 error (EXIT_CANCELED, errno, _("failed to get current context"));
172 fputs (cur_context, stdout);
173 fputc ('\n', stdout);
174 return EXIT_SUCCESS;
175 }
176
177 if (!(user || role || type || range || compute_trans))
178 {
179 if (optind >= argc)
180 {
181 error (0, 0, _("you must specify -c, -t, -u, -l, -r, or context"));
182 usage (EXIT_CANCELED);
183 }
184 context = argv[optind++];
185 }
186
187 if (optind >= argc)
188 {
189 error (0, 0, _("no command specified"));
190 usage (EXIT_CANCELED);
191 }
192
193 if (is_selinux_enabled () != 1)
194 error (EXIT_CANCELED, 0, _("%s may be used only on a SELinux kernel"),
195 program_name);
196
197 if (context)
198 {
199 con = context_new (context);
200 if (!con)
201 error (EXIT_CANCELED, errno, _("failed to create security context: %s"),
202 quote (context));
203 }
204 else
205 {
206 if (getcon (&cur_context) < 0)
207 error (EXIT_CANCELED, errno, _("failed to get current context"));
208
209 /* We will generate context based on process transition */
210 if (compute_trans)
211 {
212 /* Get context of file to be executed */
213 if (getfilecon (argv[optind], &file_context) == -1)
214 error (EXIT_CANCELED, errno,
215 _("failed to get security context of %s"),
216 quoteaf (argv[optind]));
217 /* compute result of process transition */
218 if (security_compute_create (cur_context, file_context,
219 string_to_security_class ("process"),
220 &new_context) != 0)
221 error (EXIT_CANCELED, errno, _("failed to compute a new context"));
222 /* free contexts */
223 freecon (file_context);
224 freecon (cur_context);
225
226 /* set cur_context equal to new_context */
227 cur_context = new_context;
228 }
229
230 con = context_new (cur_context);
231 if (!con)
232 error (EXIT_CANCELED, errno, _("failed to create security context: %s"),
233 quote (cur_context));
234 if (user && context_user_set (con, user))
235 error (EXIT_CANCELED, errno, _("failed to set new user: %s"),
236 quote (user));
237 if (type && context_type_set (con, type))
238 error (EXIT_CANCELED, errno, _("failed to set new type: %s"),
239 quote (type));
240 if (range && context_range_set (con, range))
241 error (EXIT_CANCELED, errno, _("failed to set new range: %s"),
242 quote (range));
243 if (role && context_role_set (con, role))
244 error (EXIT_CANCELED, errno, _("failed to set new role: %s"),
245 quote (role));
246 }
247
248 if (security_check_context (context_str (con)) < 0)
249 error (EXIT_CANCELED, errno, _("invalid context: %s"),
250 quote (context_str (con)));
251
252 if (setexeccon (context_str (con)) != 0)
253 error (EXIT_CANCELED, errno, _("unable to set security context %s"),
254 quote (context_str (con)));
255 if (cur_context != nullptr)
256 freecon (cur_context);
257
258 (compute_trans ? execv : execvp) (argv[optind], argv + optind);
259
260 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
261 error (0, errno, "%s", quote (argv[optind]));
262 return exit_status;
263 }
264