00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <config.h>
00026
00027 #include "sefs_internal.hh"
00028 #include "new_ftw.h"
00029
00030 #include <sefs/entry.hh>
00031 #include <sefs/filesystem.hh>
00032 #include <apol/util.h>
00033 #include <selinux/context.h>
00034 #include <selinux/selinux.h>
00035 #include <assert.h>
00036 #include <errno.h>
00037 #include <mntent.h>
00038 #include <regex.h>
00039 #include <stdio.h>
00040 #include <string.h>
00041 #include <unistd.h>
00042 #include <sys/stat.h>
00043 #include <sys/types.h>
00044
00045 extern int lgetfilecon_raw(const char *, security_context_t *) __attribute__ ((weak));
00046
00047
00048
00049
00050
00051
00052
00053 static int filesystem_lgetfilecon(const char *path, security_context_t * context)
00054 {
00055 if (lgetfilecon_raw != NULL)
00056 {
00057 return lgetfilecon_raw(path, context);
00058 }
00059 else
00060 {
00061 return lgetfilecon(path, context);
00062 }
00063 }
00064
00065 #if 0
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086 static apol_vector_t *filesystem_find_mount_points(const char *dir) throw(std::bad_alloc, std::runtime_error)
00087 {
00088 char *dirdup = NULL;
00089 apol_vector_t *v = NULL;
00090 FILE *mtab = NULL;
00091 struct mntent *entry;
00092
00093 try
00094 {
00095 if ((dirdup = strdup(dir)) == NULL)
00096 {
00097 throw std::bad_alloc();
00098 }
00099 size_t len = strlen(dirdup);
00100 if (len > 1 && dirdup[len - 1] == '/')
00101 {
00102 dirdup[len - 1] = '\0';
00103 }
00104 if ((v = apol_vector_create(free)) == NULL)
00105 {
00106 throw std::bad_alloc();
00107 }
00108 if ((mtab = fopen("/etc/mtab", "r")) == NULL)
00109 {
00110 throw std::runtime_error(strerror(errno));
00111 }
00112
00113 while ((entry = getmntent(mtab)) != NULL)
00114 {
00115 if (strstr(entry->mnt_dir, dir) != entry->mnt_dir)
00116 {
00117 continue;
00118 }
00119 if (strcmp(entry->mnt_dir, dirdup) == 0)
00120 {
00121 continue;
00122 }
00123 if (strstr(entry->mnt_opts, "bind") != NULL)
00124 {
00125 char *s = strdup(entry->mnt_dir);
00126 if (s == NULL)
00127 {
00128 throw std::bad_alloc();
00129 }
00130 if (apol_vector_append(v, s) < 0)
00131 {
00132 free(s);
00133 throw std::bad_alloc();
00134 }
00135 }
00136 }
00137
00138 }
00139 catch(...)
00140 {
00141 free(dirdup);
00142 apol_vector_destroy(&v);
00143 if (mtab != NULL)
00144 {
00145 fclose(mtab);
00146 }
00147 throw;
00148 }
00149 free(dirdup);
00150 fclose(mtab);
00151 return v;
00152 }
00153
00154 #endif
00155
00156
00157
00158 sefs_filesystem::sefs_filesystem(const char *new_root, sefs_callback_fn_t msg_callback, void *varg)throw(std::bad_alloc, std::invalid_argument, std::runtime_error):sefs_fclist(SEFS_FCLIST_TYPE_FILESYSTEM,
00159 msg_callback,
00160 varg)
00161 {
00162 if (new_root == NULL)
00163 {
00164 SEFS_ERR(this, "%s", strerror(EINVAL));
00165 errno = EINVAL;
00166 throw std::invalid_argument(strerror(EINVAL));
00167 }
00168 _root = NULL;
00169 _mls = false;
00170 try
00171 {
00172
00173 struct stat64 sb;
00174 if (stat64(new_root, &sb) != 0 && !S_ISDIR(sb.st_mode))
00175 {
00176 SEFS_ERR(this, "%s", strerror(EINVAL));
00177 errno = EINVAL;
00178 throw std::invalid_argument(strerror(EINVAL));
00179 }
00180
00181
00182 security_context_t scon;
00183 if (filesystem_lgetfilecon(new_root, &scon) < 0)
00184 {
00185 SEFS_ERR(this, "Could not read SELinux file context for %s.", new_root);
00186 throw std::runtime_error(strerror(errno));
00187 }
00188 context_t con;
00189 if ((con = context_new(scon)) == 0)
00190 {
00191 SEFS_ERR(this, "%s", strerror(errno));
00192 freecon(scon);
00193 throw std::runtime_error(strerror(errno));
00194 }
00195 freecon(scon);
00196 const char *range = context_range_get(con);
00197 if (range != NULL && range[0] != '\0')
00198 {
00199 _mls = true;
00200 }
00201 context_free(con);
00202
00203 if ((_root = strdup(new_root)) == NULL)
00204 {
00205 SEFS_ERR(this, "%s", strerror(errno));
00206 throw std::bad_alloc();
00207 }
00208 }
00209 catch(...)
00210 {
00211 free(_root);
00212 throw;
00213 }
00214 }
00215
00216 sefs_filesystem::~sefs_filesystem()
00217 {
00218 free(_root);
00219 }
00220
00221 struct filesystem_ftw_struct
00222 {
00223 sefs_filesystem *fs;
00224 sefs_query *query;
00225 apol_vector_t *dev_map;
00226 apol_vector_t *type_list;
00227 apol_mls_range_t *range;
00228 sefs_fclist_map_fn_t fn;
00229 void *data;
00230 bool aborted;
00231 int retval;
00232 };
00233
00234
00235
00236 inline struct sefs_context_node *filesystem_get_context(sefs_filesystem * fs, security_context_t scon) throw(std::bad_alloc)
00237 {
00238 return fs->getContext(scon);
00239 }
00240
00241 inline sefs_entry *filesystem_get_entry(sefs_filesystem * fs, const struct sefs_context_node * node, uint32_t objClass,
00242 const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
00243 {
00244 return fs->getEntry(node, objClass, path, ino, dev_name);
00245 }
00246
00247 inline bool filesystem_is_query_match(sefs_filesystem * fs, const sefs_query * query, const char *path, const char *dev,
00248 const struct stat64 * sb, apol_vector_t * type_list,
00249 apol_mls_range_t * range)throw(std::runtime_error)
00250 {
00251 return fs->isQueryMatch(query, path, dev, sb, type_list, range);
00252 }
00253
00254 static uint32_t filesystem_stat_to_objclass(const struct stat64 *sb)
00255 {
00256 if (S_ISREG(sb->st_mode))
00257 {
00258 return QPOL_CLASS_FILE;
00259 }
00260 if (S_ISDIR(sb->st_mode))
00261 {
00262 return QPOL_CLASS_DIR;
00263 }
00264 if (S_ISCHR(sb->st_mode))
00265 {
00266 return QPOL_CLASS_CHR_FILE;
00267 }
00268 if (S_ISBLK(sb->st_mode))
00269 {
00270 return QPOL_CLASS_BLK_FILE;
00271 }
00272 if (S_ISFIFO(sb->st_mode))
00273 {
00274 return QPOL_CLASS_FIFO_FILE;
00275 }
00276 if (S_ISLNK(sb->st_mode))
00277 {
00278 return QPOL_CLASS_LNK_FILE;
00279 }
00280 if (S_ISSOCK(sb->st_mode))
00281 {
00282 return QPOL_CLASS_SOCK_FILE;
00283 }
00284 assert(0);
00285 return 0;
00286 }
00287
00288 struct filesystem_dev
00289 {
00290 dev_t dev;
00291 char *dev_name;
00292 };
00293
00294 static int filesystem_dev_cmp(const void *a, const void *b __attribute__ ((unused)), void *arg)
00295 {
00296 const struct filesystem_dev *d1 = static_cast < const struct filesystem_dev *>(a);
00297 dev_t *d2 = static_cast < dev_t * >(arg);
00298 if (d1->dev < *d2)
00299 {
00300 return -1;
00301 }
00302 else if (d1->dev > *d2)
00303 {
00304 return 1;
00305 }
00306 return 0;
00307 }
00308
00309 static int filesystem_ftw_handler(const char *fpath, const struct stat64 *sb, int typeflag
00310 __attribute__ ((unused)), struct FTW *ftwbuf __attribute__ ((unused)), void *data)
00311 {
00312 struct filesystem_ftw_struct *s = static_cast < struct filesystem_ftw_struct *>(data);
00313
00314 size_t i;
00315 void *dev_num = const_cast < void *>(static_cast < const void *>(&(sb->st_dev)));
00316 int rc = apol_vector_get_index(s->dev_map, NULL, filesystem_dev_cmp, dev_num, &i);
00317 const char *dev = "<unknown>";
00318 if (rc == 0)
00319 {
00320
00321
00322 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(s->dev_map, i));
00323 dev = d->dev_name;
00324 }
00325 else
00326 {
00327 SEFS_WARN(s->fs, "Unknown device for %s.", fpath);
00328 }
00329 try
00330 {
00331 if (!filesystem_is_query_match(s->fs, s->query, fpath, dev, sb, s->type_list, s->range))
00332 {
00333 return 0;
00334 }
00335 }
00336 catch(...)
00337 {
00338 return -1;
00339 }
00340
00341 security_context_t scon;
00342 if (filesystem_lgetfilecon(fpath, &scon) < 0)
00343 {
00344 SEFS_ERR(s->fs, "Could not read SELinux file context for %s.", fpath);
00345 return -1;
00346 }
00347 struct sefs_context_node *node = NULL;
00348 try
00349 {
00350 node = filesystem_get_context(s->fs, scon);
00351 }
00352 catch(...)
00353 {
00354 freecon(scon);
00355 return -1;
00356 }
00357 freecon(scon);
00358
00359 uint32_t objClass = filesystem_stat_to_objclass(sb);
00360
00361 sefs_entry *entry = NULL;
00362 try
00363 {
00364 entry = filesystem_get_entry(s->fs, node, objClass, fpath, sb->st_ino, dev);
00365 }
00366 catch(...)
00367 {
00368 return -1;
00369 }
00370
00371
00372 s->retval = s->fn(s->fs, entry, s->data);
00373 delete entry;
00374 if (s->retval < 0)
00375 {
00376 s->aborted = true;
00377 return s->retval;
00378 }
00379
00380 return 0;
00381 }
00382
00383 int sefs_filesystem::runQueryMap(sefs_query * query, sefs_fclist_map_fn_t fn, void *data) throw(std::runtime_error,
00384 std::invalid_argument)
00385 {
00386 struct filesystem_ftw_struct s;
00387 s.dev_map = NULL;
00388 s.type_list = NULL;
00389 s.range = NULL;
00390 try
00391 {
00392 s.dev_map = buildDevMap();
00393 if (query != NULL)
00394 {
00395 query->compile();
00396 if (policy != NULL)
00397 {
00398 if (query->_type != NULL && query->_indirect &&
00399 (s.type_list =
00400 query_create_candidate_type(policy, query->_type, query->_retype, query->_regex,
00401 query->_indirect)) == NULL)
00402 {
00403 SEFS_ERR(this, "%s", strerror(errno));
00404 throw std::runtime_error(strerror(errno));
00405 }
00406 if (query->_range != NULL && query->_rangeMatch != 0 &&
00407 (s.range = apol_mls_range_create_from_string(policy, query->_range)) == NULL)
00408 {
00409 SEFS_ERR(this, "%s", strerror(errno));
00410 throw std::runtime_error(strerror(errno));
00411 }
00412 }
00413 }
00414 }
00415 catch(...)
00416 {
00417 apol_vector_destroy(&s.dev_map);
00418 apol_vector_destroy(&s.type_list);
00419 apol_mls_range_destroy(&s.range);
00420 throw;
00421 }
00422 s.fs = this;
00423 s.query = query;
00424 s.fn = fn;
00425 s.data = data;
00426 s.aborted = false;
00427 s.retval = 0;
00428
00429 int retval = new_nftw64(_root, filesystem_ftw_handler, 1024, 0, &s);
00430 apol_vector_destroy(&s.dev_map);
00431 apol_vector_destroy(&s.type_list);
00432 apol_mls_range_destroy(&s.range);
00433 if (retval != 0 && !s.aborted)
00434 {
00435
00436
00437 return retval;
00438 }
00439 return s.retval;
00440 }
00441
00442 bool sefs_filesystem::isMLS() const
00443 {
00444 return _mls;
00445 }
00446
00447 const char *sefs_filesystem::root() const
00448 {
00449 return _root;
00450 }
00451
00452
00453
00454 static void filesystem_dev_free(void *elem)
00455 {
00456 if (elem != NULL)
00457 {
00458 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(elem);
00459
00460
00461 free(d);
00462 }
00463 }
00464
00465 const char *sefs_filesystem::getDevName(const dev_t dev) throw(std::runtime_error)
00466 {
00467 apol_vector_t *dev_map = buildDevMap();
00468 size_t i;
00469 void *devp = const_cast < dev_t * >(&dev);
00470 int rc = apol_vector_get_index(dev_map, NULL, filesystem_dev_cmp, devp, &i);
00471 if (rc < 0)
00472 {
00473 apol_vector_destroy(&dev_map);
00474 return NULL;
00475 }
00476 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(apol_vector_get_element(dev_map, i));
00477 const char *dev_name = d->dev_name;
00478 apol_vector_destroy(&dev_map);
00479 return dev_name;
00480 }
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 apol_vector_t *sefs_filesystem::buildDevMap(void)throw(std::runtime_error)
00493 {
00494 apol_vector_t *dev_map;
00495 if ((dev_map = apol_vector_create(filesystem_dev_free)) == NULL)
00496 {
00497 SEFS_ERR(this, "%s", strerror(errno));
00498 throw std::runtime_error(strerror(errno));
00499 }
00500 FILE *f = NULL;
00501 try
00502 {
00503 if ((f = fopen("/etc/mtab", "r")) == NULL)
00504 {
00505 SEFS_ERR(this, "%s", strerror(errno));
00506 throw std::runtime_error(strerror(errno));
00507 }
00508 char buf[256];
00509 struct mntent mntbuf;
00510 while (getmntent_r(f, &mntbuf, buf, 256) != NULL)
00511 {
00512 struct stat sb;
00513 if (stat(mntbuf.mnt_dir, &sb) == -1)
00514 {
00515
00516
00517
00518 continue;
00519 }
00520 else
00521 {
00522 struct filesystem_dev *d = static_cast < struct filesystem_dev *>(calloc(1, sizeof(*d)));
00523 if (d == NULL)
00524 {
00525 SEFS_ERR(this, "%s", strerror(errno));
00526 throw std::runtime_error(strerror(errno));
00527 }
00528 if (apol_vector_append(dev_map, d) < 0)
00529 {
00530 SEFS_ERR(this, "%s", strerror(errno));
00531 filesystem_dev_free(d);
00532 throw std::runtime_error(strerror(errno));
00533 }
00534 d->dev = sb.st_dev;
00535 char *mnt_fsname = strdup(mntbuf.mnt_fsname);
00536 if (mnt_fsname == NULL)
00537 {
00538 SEFS_ERR(this, "%s", strerror(errno));
00539 throw std::runtime_error(strerror(errno));
00540 }
00541 if (apol_bst_insert_and_get(dev_tree, (void **)&mnt_fsname, NULL) < 0)
00542 {
00543 SEFS_ERR(this, "%s", strerror(errno));
00544 free(mnt_fsname);
00545 throw std::runtime_error(strerror(errno));
00546 }
00547 d->dev_name = mnt_fsname;
00548 }
00549 }
00550 }
00551 catch(...)
00552 {
00553 apol_vector_destroy(&dev_map);
00554 if (f != NULL)
00555 {
00556 fclose(f);
00557 }
00558 throw;
00559 }
00560 fclose(f);
00561 return dev_map;
00562 }
00563
00564 bool sefs_filesystem::isQueryMatch(const sefs_query * query, const char *path, const char *dev, const struct stat64 * sb,
00565 apol_vector_t * type_list, apol_mls_range_t * range)throw(std::runtime_error)
00566 {
00567 if (query == NULL)
00568 {
00569 return true;
00570 }
00571 security_context_t scon;
00572 if (filesystem_lgetfilecon(path, &scon) < 0)
00573 {
00574 SEFS_ERR(this, "%s", strerror(errno));
00575 throw std::runtime_error(strerror(errno));
00576 }
00577 context_t con;
00578 if ((con = context_new(scon)) == 0)
00579 {
00580 SEFS_ERR(this, "%s", strerror(errno));
00581 freecon(scon);
00582 throw std::runtime_error(strerror(errno));
00583 }
00584 freecon(scon);
00585
00586 if (!query_str_compare(context_user_get(con), query->_user, query->_reuser, query->_regex))
00587 {
00588 context_free(con);
00589 return false;
00590 }
00591 if (!query_str_compare(context_role_get(con), query->_role, query->_rerole, query->_regex))
00592 {
00593 context_free(con);
00594 return false;
00595 }
00596
00597 bool str_matched = false, pol_matched = false;
00598 str_matched = query_str_compare(context_type_get(con), query->_type, query->_retype, query->_regex);
00599 if (type_list != NULL && !str_matched)
00600 {
00601 size_t index;
00602 pol_matched = (apol_vector_get_index(type_list, context_type_get(con), apol_str_strcmp, NULL, &index) < 0);
00603 }
00604 if (!str_matched && !pol_matched)
00605 {
00606 context_free(con);
00607 return false;
00608 }
00609
00610 if (isMLS())
00611 {
00612 if (range == NULL)
00613 {
00614 if (!query_str_compare(context_range_get(con), query->_range, query->_rerange, query->_regex))
00615 {
00616 context_free(con);
00617 return false;
00618 }
00619 }
00620 else
00621 {
00622 assert(policy != NULL);
00623 apol_mls_range_t *context_range = apol_mls_range_create_from_string(policy, context_range_get(con));
00624 if (context_range == NULL)
00625 {
00626 SEFS_ERR(this, "%s", strerror(errno));
00627 context_free(con);
00628 throw std::runtime_error(strerror(errno));
00629 }
00630 int ret;
00631 ret = apol_mls_range_compare(policy, range, context_range, query->_rangeMatch);
00632 apol_mls_range_destroy(&context_range);
00633 if (ret <= 0)
00634 {
00635 context_free(con);
00636 return false;
00637 }
00638 }
00639 }
00640
00641 context_free(con);
00642
00643 if (query->_objclass != 0 && query->_objclass != filesystem_stat_to_objclass(sb))
00644 {
00645 return false;
00646 }
00647
00648 if (!query_str_compare(path, query->_path, query->_repath, query->_regex))
00649 {
00650 return false;
00651 }
00652
00653 if (query->_inode != 0 && query->_inode != sb->st_ino)
00654 {
00655 return false;
00656 }
00657
00658 if (!query_str_compare(dev, query->_dev, query->_redev, query->_regex))
00659 {
00660 return false;
00661 }
00662
00663 return true;
00664 }
00665
00666 sefs_entry *sefs_filesystem::getEntry(const struct sefs_context_node * context, uint32_t objectClass,
00667 const char *path, ino64_t ino, const char *dev_name)throw(std::bad_alloc)
00668 {
00669 char *s = strdup(path);
00670 if (s == NULL)
00671 {
00672 SEFS_ERR(this, "%s", strerror(errno));
00673 throw std::bad_alloc();
00674 }
00675 if (apol_bst_insert_and_get(path_tree, (void **)&s, NULL) < 0)
00676 {
00677 SEFS_ERR(this, "%s", strerror(errno));
00678 free(s);
00679 throw std::bad_alloc();
00680 }
00681 sefs_entry *e = new sefs_entry(this, context, objectClass, s);
00682 e->_inode = ino;
00683 e->_dev = dev_name;
00684 return e;
00685 }
00686
00687
00688
00689 sefs_filesystem_t *sefs_filesystem_create(const char *root, sefs_callback_fn_t msg_callback, void *varg)
00690 {
00691 sefs_filesystem_t *fs;
00692 try
00693 {
00694 fs = new sefs_filesystem(root, msg_callback, varg);
00695 }
00696 catch(...)
00697 {
00698 errno = ENOMEM;
00699 return NULL;
00700 }
00701 return fs;
00702 }
00703
00704 const char *sefs_filesystem_get_root(const sefs_filesystem_t * fs)
00705 {
00706 if (fs == NULL)
00707 {
00708 SEFS_ERR(NULL, "%s", strerror(EINVAL));
00709 errno = EINVAL;
00710 return NULL;
00711 }
00712 return fs->root();
00713 }
00714
00715 extern const char *sefs_filesystem_get_dev_name(sefs_filesystem_t * fs, const dev_t dev)
00716 {
00717 if (fs == NULL)
00718 {
00719 SEFS_ERR(NULL, "%s", strerror(EINVAL));
00720 errno = EINVAL;
00721 return NULL;
00722 }
00723 const char *dev_name = NULL;
00724 try
00725 {
00726 dev_name = fs->getDevName(dev);
00727 }
00728 catch(...)
00729 {
00730 return NULL;
00731 }
00732 return dev_name;
00733 }