dd: clarify meaning of multiplication factors; put xM in order
[platform/upstream/coreutils.git] / src / runcon.c
1 /* runcon -- run command with specified security context
2    Copyright (C) 2005-2008 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 <http://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 #ifdef HAVE_SELINUX_FLASK_H
49 # include <selinux/flask.h>
50 #else
51 # define SECCLASS_PROCESS 0
52 #endif
53 #include <sys/types.h>
54 #include "system.h"
55 #include "error.h"
56 #include "quote.h"
57 #include "quotearg.h"
58
59 /* The official name of this program (e.g., no `g' prefix).  */
60 #define PROGRAM_NAME "runcon"
61
62 #define AUTHORS proper_name ("Russell Coker")
63
64 static struct option const long_options[] =
65 {
66   {"role", required_argument, NULL, 'r'},
67   {"type", required_argument, NULL, 't'},
68   {"user", required_argument, NULL, 'u'},
69   {"range", required_argument, NULL, 'l'},
70   {"compute", no_argument, NULL, 'c'},
71   {GETOPT_HELP_OPTION_DECL},
72   {GETOPT_VERSION_OPTION_DECL},
73   {NULL, 0, NULL, 0}
74 };
75
76 void
77 usage (int status)
78 {
79   if (status != EXIT_SUCCESS)
80     fprintf (stderr, _("Try `%s --help' for more information.\n"),
81              program_name);
82   else
83     {
84       printf (_("\
85 Usage: %s CONTEXT COMMAND [args]\n\
86   or:  %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\
87 "), program_name, program_name);
88       fputs (_("\
89 Run a program in a different security context.\n\
90 With neither CONTEXT nor COMMAND, print the current security context.\n\
91 \n\
92   CONTEXT            Complete security context\n\
93   -c, --compute      compute process transition context before modifying\n\
94   -t, --type=TYPE    type (for same role as parent)\n\
95   -u, --user=USER    user identity\n\
96   -r, --role=ROLE    role\n\
97   -l, --range=RANGE  levelrange\n\
98 \n\
99 "), stdout);
100       fputs (HELP_OPTION_DESCRIPTION, stdout);
101       fputs (VERSION_OPTION_DESCRIPTION, stdout);
102       emit_bug_reporting_address ();
103     }
104   exit (status);
105 }
106
107 int
108 main (int argc, char **argv)
109 {
110   char *role = NULL;
111   char *range = NULL;
112   char *user = NULL;
113   char *type = NULL;
114   char *context = NULL;
115   security_context_t cur_context = NULL;
116   security_context_t file_context = NULL;
117   security_context_t new_context = NULL;
118   bool compute_trans = false;
119
120   context_t con;
121
122   initialize_main (&argc, &argv);
123   set_program_name (argv[0]);
124   setlocale (LC_ALL, "");
125   bindtextdomain (PACKAGE, LOCALEDIR);
126   textdomain (PACKAGE);
127
128   atexit (close_stdout);
129
130   while (1)
131     {
132       int option_index = 0;
133       int c = getopt_long (argc, argv, "+r:t:u:l:c", long_options,
134                            &option_index);
135       if (c == -1)
136         break;
137       switch (c)
138         {
139         case 'r':
140           if (role)
141             error (EXIT_FAILURE, 0, _("multiple roles"));
142           role = optarg;
143           break;
144         case 't':
145           if (type)
146             error (EXIT_FAILURE, 0, _("multiple types"));
147           type = optarg;
148           break;
149         case 'u':
150           if (user)
151             error (EXIT_FAILURE, 0, _("multiple users"));
152           user = optarg;
153           break;
154         case 'l':
155           if (range)
156             error (EXIT_FAILURE, 0, _("multiple levelranges"));
157           range = optarg;
158           break;
159         case 'c':
160           compute_trans = true;
161           break;
162
163         case_GETOPT_HELP_CHAR;
164         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
165         default:
166           usage (EXIT_FAILURE);
167           break;
168         }
169     }
170
171   if (argc - optind == 0)
172     {
173       if (getcon (&cur_context) < 0)
174         error (EXIT_FAILURE, errno, _("failed to get current context"));
175       fputs (cur_context, stdout);
176       fputc ('\n', stdout);
177       exit (EXIT_SUCCESS);
178     }
179
180   if (!(user || role || type || range || compute_trans))
181     {
182       if (optind >= argc)
183         {
184           error (0, 0, _("you must specify -c, -t, -u, -l, -r, or context"));
185           usage (1);
186         }
187       context = argv[optind++];
188     }
189
190   if (optind >= argc)
191     {
192       error (0, 0, _("no command specified"));
193       usage (1);
194     }
195
196   if (is_selinux_enabled () != 1)
197     error (EXIT_FAILURE, 0,
198            _("runcon may be used only on a SELinux kernel"));
199
200   if (context)
201     {
202       con = context_new (context);
203       if (!con)
204         error (EXIT_FAILURE, errno, _("failed to create security context: %s"),
205                quotearg_colon (context));
206     }
207   else
208     {
209       if (getcon (&cur_context) < 0)
210         error (EXIT_FAILURE, errno, _("failed to get current context"));
211
212       /* We will generate context based on process transition */
213       if (compute_trans)
214         {
215           /* Get context of file to be executed */
216           if (getfilecon (argv[optind], &file_context) == -1)
217             error (EXIT_FAILURE, errno,
218                    _("failed to get security context of %s"),
219                    quote (argv[optind]));
220           /* compute result of process transition */
221           if (security_compute_create (cur_context, file_context,
222                                        SECCLASS_PROCESS, &new_context) != 0)
223             error (EXIT_FAILURE, errno,
224                    _("failed to compute a new context"));
225           /* free contexts */
226           freecon (file_context);
227           freecon (cur_context);
228
229           /* set cur_context equal to new_context */
230           cur_context = new_context;
231         }
232
233       con = context_new (cur_context);
234       if (!con)
235         error (EXIT_FAILURE, errno, _("failed to create security context: %s"),
236                quotearg_colon (cur_context));
237       if (user && context_user_set (con, user))
238         error (EXIT_FAILURE, errno, _("failed to set new user %s"), user);
239       if (type && context_type_set (con, type))
240         error (EXIT_FAILURE, errno, _("failed to set new type %s"), type);
241       if (range && context_range_set (con, range))
242         error (EXIT_FAILURE, errno, _("failed to set new range %s"), range);
243       if (role && context_role_set (con, role))
244         error (EXIT_FAILURE, errno, _("failed to set new role %s"), role);
245     }
246
247   if (security_check_context (context_str (con)) < 0)
248     error (EXIT_FAILURE, errno, _("invalid context: %s"),
249            quotearg_colon (context_str (con)));
250
251   if (setexeccon (context_str (con)) != 0)
252     error (EXIT_FAILURE, errno, _("unable to set security context %s"),
253            quote (context_str (con)));
254   if (cur_context != NULL)
255     freecon (cur_context);
256
257   execvp (argv[optind], argv + optind);
258
259   {
260     int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
261     error (0, errno, "%s", argv[optind]);
262     exit (exit_status);
263   }
264 }