replcon.cc

Go to the documentation of this file.
00001 /**
00002  * @file
00003  *
00004  * A tool for replacing file contexts in SELinux.
00005  *
00006  *  @author Jeremy A. Mowery jmowery@tresys.com
00007  *  @author Jason Tang jtang@tresys.com
00008  *
00009  * Copyright (C) 2003-2007 Tresys Technology, LLC
00010  *
00011  *  This program is free software; you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation; either version 2 of the License, or
00014  *  (at your option) any later version.
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public License
00022  *  along with this program; if not, write to the Free Software
00023  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00024  */
00025 
00026 #include <config.h>
00027 
00028 #include <sefs/filesystem.hh>
00029 #include <sefs/query.hh>
00030 #include <selinux/selinux.h>
00031 #include <apol/util.h>
00032 
00033 using namespace std;
00034 
00035 #include <assert.h>
00036 #include <errno.h>
00037 #include <getopt.h>
00038 #include <iostream>
00039 #include <stdlib.h>
00040 
00041 #define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC"
00042 
00043 enum OPTIONS
00044 {
00045         OPTION_CONTEXT = 256
00046 };
00047 
00048 static struct option const longopts[] = {
00049         {"class", required_argument, NULL, 'c'},
00050         {"type", required_argument, NULL, 't'},
00051         {"user", required_argument, NULL, 'u'},
00052         {"role", required_argument, NULL, 'r'},
00053         {"mls-range", required_argument, NULL, 'm'},
00054         {"path", required_argument, NULL, 'p'},
00055         {"regex", no_argument, NULL, 'R'},
00056         {"context", required_argument, NULL, OPTION_CONTEXT},
00057         {"verbose", no_argument, NULL, 'v'},
00058         {"help", no_argument, NULL, 'h'},
00059         {"version", no_argument, NULL, 'V'},
00060         {NULL, 0, NULL, 0}
00061 };
00062 
00063 extern int lsetfilecon_raw(const char *, security_context_t) __attribute__ ((weak));
00064 
00065 /**
00066  * As that setools must work with older libselinux versions that may
00067  * not have the _raw() functions, declare them as weak.  If libselinux
00068  * does indeed have the new functions then use them; otherwise
00069  * fallback to the originals.
00070  */
00071 static int replcon_lsetfilecon(const char *path, security_context_t context)
00072 {
00073         if (lsetfilecon_raw != NULL)
00074         {
00075                 return lsetfilecon_raw(path, context);
00076         }
00077         else
00078         {
00079                 return lsetfilecon(path, context);
00080         }
00081 }
00082 
00083 struct replcon_info
00084 {
00085         bool verbose, mls;
00086         apol_context_t *replcon;
00087 };
00088 
00089 static void usage(const char *program_name, bool brief)
00090 {
00091         cout << "Usage: " << program_name << " NEW_CONTEXT DIR [OPTIONS] [EXPRESSION]" << endl << endl;
00092         if (brief)
00093         {
00094                 cout << "\tTry " << program_name << " --help for more help." << endl << endl;
00095                 return;
00096         }
00097 
00098         cout << "Replace SELinux file contexts for files matching a given context." << endl << endl;
00099 
00100         cout << "REQUIRED ARGUMENTS :" << endl;
00101         cout << "  NEW_CONTEXT                    partial or full context to relabel" << endl;
00102         cout << "  DIR                            starting directory to replace" << endl;
00103         cout << endl;
00104         cout << "EXPRESSION:" << endl;
00105         cout << "  -t TYPE,  --type=TYPE          find contexts with type TYPE" << endl;
00106         cout << "  -u USER,  --user=USER          find contexts with user USER" << endl;
00107         cout << "  -r ROLE,  --role=ROLE          find contexts with role ROLE" << endl;
00108         cout << "  -m RANGE, --mls-range=RANGE    find contexts with MLS range RANGE" << endl;
00109         cout << "  --context=CONTEXT              partial or full context to find" << endl;
00110         cout << "                                 (overrides expression options above)" << endl;
00111         cout << "  -p PATH,  --path=PATH          find files in PATH" << endl;
00112         cout << "  -c CLASS, --class=CLASS        find files of object class CLASS" << endl;
00113         cout << endl;
00114 
00115         cout << "OPTIONS:" << endl;
00116         cout << "  -R, --regex                    enable regular expressions" << endl;
00117         cout << "  -v, --verbose                  show context of matching files" << endl;
00118         cout << "  -h, --help                     print this help text and exit" << endl;
00119         cout << "  -V, --version                  print version information and exit" << endl;
00120         cout << endl;
00121         cout << "If the fclist does not contain MLS ranges and -m was given," << endl;
00122         cout << "then the search will return nothing." << endl;
00123         cout << endl;
00124         cout << "NEW_CONTEXT is as a colon separated list of user, role, type, and MLS range" << endl;
00125         cout << "such as follows: user_u:object_r:user_t:s0.  If a field is not specified," << endl;
00126         cout << "that portion of the context will not be replaced." << endl;
00127         cout << "Examples:" << endl;
00128         cout << "    replcon ::type_t: ." << endl;
00129         cout << "        Replace all files and subdirectories in current directory with" << endl;
00130         cout << "        type type_t, recursing within the directory." << endl;
00131         cout << "    replcon -u user_u *:role_r:* ." << endl;
00132         cout << "        Replace files that contain user_u with role role_r." << endl;
00133         cout << "    replcon --context ::type_t:so :::s0:c0 /tmp" << endl;
00134         cout << "        Replace files with type type_t and level s0 in /tmp with MLS" << endl;
00135         cout << "        range s0:c0." << endl;
00136 }
00137 
00138 static int replace_entry(sefs_fclist * fclist, const sefs_entry * e, void *arg)
00139 {
00140         struct replcon_info *r = static_cast < struct replcon_info *>(arg);
00141         const apol_context_t *scon = e->context();
00142         const char *user, *role, *type;
00143         char *con_str = NULL;
00144         size_t len = 0;
00145 
00146         // determine what the new context should be
00147         if ((user = apol_context_get_user(r->replcon)) == NULL)
00148         {
00149                 user = apol_context_get_user(scon);
00150         }
00151         if ((role = apol_context_get_role(r->replcon)) == NULL)
00152         {
00153                 role = apol_context_get_role(scon);
00154         }
00155         if ((type = apol_context_get_type(r->replcon)) == NULL)
00156         {
00157                 type = apol_context_get_type(scon);
00158         }
00159         if (apol_str_appendf(&con_str, &len, "%s:%s:%s", user, role, type) < 0)
00160         {
00161                 return -1;
00162         }
00163         if (r->mls)
00164         {
00165                 const apol_mls_range_t *apol_range = NULL;
00166                 char *range = NULL;
00167                 if ((apol_range = apol_context_get_range(r->replcon)) == NULL)
00168                 {
00169                         apol_range = apol_context_get_range(scon);
00170                 }
00171                 if ((range = apol_mls_range_render(NULL, apol_range)) == NULL || apol_str_appendf(&con_str, &len, ":%s", range) < 0)
00172                 {
00173                         free(range);
00174                         free(con_str);
00175                         return -1;
00176                 }
00177                 free(range);
00178         }
00179 
00180         if (r->verbose)
00181         {
00182                 char *lcon = NULL, *rcon = NULL;
00183                 if (r->mls)
00184                 {
00185                         lcon = apol_context_render(NULL, r->replcon);
00186                         rcon = apol_context_render(NULL, scon);
00187                 }
00188                 else
00189                 {
00190                         if (asprintf(&lcon, "%s:%s:%s",
00191                                      apol_context_get_user(r->replcon),
00192                                      apol_context_get_role(r->replcon), apol_context_get_type(r->replcon)) < 0)
00193                         {
00194                                 lcon = NULL;
00195                         }
00196                         if (asprintf(&rcon, "%s:%s:%s",
00197                                      apol_context_get_user(scon), apol_context_get_role(scon), apol_context_get_type(scon)) < 0)
00198                         {
00199                                 rcon = NULL;
00200                         }
00201                 }
00202                 if (lcon == NULL || rcon == NULL)
00203                 {
00204                         free(lcon);
00205                         free(rcon);
00206                         return -1;
00207                 }
00208                 printf("%s: %s --> %s\n", e->path(), lcon, rcon);
00209                 free(lcon);
00210                 free(rcon);
00211         }
00212 
00213         // until there is a way to create a security_context_t from a
00214         // char *, simply perform the implicit cast below
00215         if (replcon_lsetfilecon(e->path(), con_str) != 0)
00216         {
00217                 cerr << "Could not set context " << con_str << " for file " << e->path() << "." << endl;
00218                 free(con_str);
00219                 return -1;
00220         }
00221 
00222         free(con_str);
00223         return 0;
00224 }
00225 
00226 int main(int argc, char *argv[])
00227 {
00228         int optc;
00229         struct replcon_info r;
00230 
00231         r.verbose = false;
00232         r.replcon = NULL;
00233         sefs_query *query = new sefs_query();
00234 
00235         apol_context_t *context = NULL;
00236         try
00237         {
00238                 while ((optc = getopt_long(argc, argv, "t:u:r:m:p:c:RvhV", longopts, NULL)) != -1)
00239                 {
00240                         switch (optc)
00241                         {
00242                         case 't':
00243                                 if (context == NULL)
00244                                 {
00245                                         query->type(optarg, false);
00246                                 }
00247                                 break;
00248                         case 'u':
00249                                 if (context == NULL)
00250                                 {
00251                                         query->user(optarg);
00252                                 }
00253                                 break;
00254                         case 'r':
00255                                 if (context == NULL)
00256                                 {
00257                                         query->role(optarg);
00258                                 }
00259                                 break;
00260                         case 'm':
00261                                 if (context == NULL)
00262                                 {
00263                                         query->range(optarg, APOL_QUERY_EXACT);
00264                                 }
00265                                 break;
00266                         case OPTION_CONTEXT:
00267                                 if ((context = apol_context_create_from_literal(optarg)) == NULL)
00268                                 {
00269                                         cerr << "Could not create source context." << endl;
00270                                         throw runtime_error(strerror(errno));
00271                                 }
00272                                 break;
00273                         case 'p':
00274                                 query->path(optarg);
00275                                 break;
00276                         case 'c':
00277                                 query->objectClass(optarg);
00278                                 break;
00279                         case 'R':
00280                                 query->regex(true);
00281                                 break;
00282                         case 'v':
00283                                 r.verbose = true;
00284                                 break;
00285                         case 'h':     // help
00286                                 usage(argv[0], false);
00287                                 exit(0);
00288                         case 'V':     // version
00289                                 cout << "replcon " << VERSION << endl << COPYRIGHT_INFO << endl;
00290                                 exit(0);
00291                         default:
00292                                 usage(argv[0], true);
00293                                 exit(1);
00294                         }
00295                         if (context != NULL)
00296                         {
00297                                 query->user(apol_context_get_user(context));
00298                                 query->role(apol_context_get_role(context));
00299                                 query->type(apol_context_get_type(context), false);
00300                                 if (apol_context_get_range(context) != NULL)
00301                                 {
00302                                         char *rng = apol_mls_range_render(NULL, apol_context_get_range(context));
00303                                         query->range(rng, APOL_QUERY_EXACT);
00304                                         free(rng);
00305                                 }
00306                                 else
00307                                 {
00308                                         query->range(NULL, APOL_QUERY_EXACT);
00309                                 }
00310                         }
00311                 }
00312         }
00313         catch(bad_alloc)
00314         {
00315                 cerr << strerror(errno) << endl;
00316                 apol_context_destroy(&context);
00317                 delete query;
00318                 exit(-1);
00319         }
00320         apol_context_destroy(&context);
00321 
00322         if (optind + 2 != argc)
00323         {
00324                 usage(argv[0], 1);
00325                 delete query;
00326                 exit(-1);
00327         }
00328 
00329         sefs_fclist *fclist = NULL;
00330         try
00331         {
00332                 fclist = new sefs_filesystem(argv[optind + 1], NULL, NULL);
00333                 r.mls = fclist->isMLS();
00334 
00335                 if ((r.replcon = apol_context_create_from_literal(argv[optind])) == NULL)
00336                 {
00337                         cerr << "Could not create replacement context." << endl;
00338                         throw runtime_error(strerror(errno));
00339                 }
00340 
00341                 if (fclist->runQueryMap(query, replace_entry, &r) < 0)
00342                 {
00343                         throw runtime_error(strerror(errno));
00344                 }
00345         }
00346         catch(...)
00347         {
00348                 delete query;
00349                 delete fclist;
00350                 apol_context_destroy(&(r.replcon));
00351                 exit(-1);
00352         }
00353 
00354         delete query;
00355         delete fclist;
00356         apol_context_destroy(&(r.replcon));
00357         return 0;
00358 }