2 * This file is part of MSM security plugin
3 * Greatly based on the code of MSSF security plugin
5 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 * Contact: Tero Aho <ext-tero.aho@nokia.com>
9 * Copyright (C) 2011 - 2013 Intel Corporation.
11 * Contact: Elena Reshetova <elena.reshetova@intel.com>
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
34 #include <sys/capability.h>
35 #include <sys/types.h>
40 #include <attr/xattr.h>
43 #include "rpmio/rpmlog.h"
44 #include "rpm/rpmlib.h"
45 #include <rpm/rpmmacro.h>
46 #include <rpm/rpmts.h>
50 static ac_domain_x *all_ac_domains = NULL; /* hash of all provided ac domains */
51 static package_x *allpackages = NULL; /* hash of all installed packages */
53 static int msmCheckDomainRequestOrPermit(manifest_x *mfx, const char* domain);
55 void msmFreeInternalHashes(void)
58 HASH_CLEAR(hh,all_ac_domains);
62 HASH_CLEAR(hh,allpackages);
66 static int msmCheckACDomainRules(ac_domain_x *ac_domain,
67 sw_source_x *requested, sw_source_x *provided)
69 sw_source_x *sw_source;
71 /* go through sw source and its parents: ac domains must not match */
72 /* deny or deny wildcards and must match allow or allow wildcards */
73 /* in the whole path up to the level of the providing sw source */
75 for (sw_source = requested; sw_source->parent && sw_source->parent != sw_source; sw_source = sw_source->parent) {
78 /* check first if requested ac domain is denied */
79 HASH_FIND(hh, sw_source->denys, ac_domain->name, strlen(ac_domain->name), denied);
80 if (denied) return 0; /* matched deny */
81 for (denied = sw_source->denymatches; denied; denied = denied->prev)
82 if (!strwcmp(denied->match, ac_domain->name))
83 return 0; /* matched deny wildcard */
85 /* not denied, now check if it's in allows or allowmatches */
86 HASH_FIND(hh, sw_source->allows, ac_domain->name, strlen(ac_domain->name), allowed);
87 if (allowed) continue; /* matched allow */
88 for (allowed = sw_source->allowmatches; allowed; allowed = allowed->prev)
89 if (!strwcmp(allowed->match, ac_domain->name))
90 break; /* matched allow wildcard */
91 if (allowed) continue; /* matched allow wildcard */
93 if (strcmp(sw_source->rankkey, provided->rankkey) <= 0)
94 return 1; /* ranked higher (or same sw source), allow */
95 return 0; /* not mentioned, deny */
97 return 1; /* still here, allow for root sw source */
100 static int msmCheckLabelProvisioning(manifest_x *mfx, const char* label)
103 d_provide_x *provide = NULL;
105 if ((mfx) && (label) && (mfx->define) && (mfx->define->d_provides)) {
106 for (provide = mfx->define->d_provides; provide; provide = provide->prev) {
107 if ( strcmp(provide->label_name, label) == 0 )
111 rpmlog(RPMLOG_ERR, "Label %s hasn't been provided in the manifest\n", label);
115 static int msmSetSmackRules(struct smack_accesses *smack_accesses, ac_domain_x *ac_domains, const char *aid)
117 ac_domain_x *ac_domain;
120 if (!smack_accesses) return ret;
122 for (ac_domain = ac_domains; ac_domain; ac_domain = ac_domain->prev) {
123 if (ac_domain->allowed) {
124 ret = smack_accesses_add(smack_accesses, aid, ac_domain->name, "rw");
126 rpmlog(RPMLOG_ERR, "smack_add failed for %s %s\n",
127 aid, ac_domain->name);
130 }/* else if (!ac_domain->allowed && !ac_domain->newer) {
131 // remove not allowed rule in case something has changed
132 smack_rule_set_remove(rule_set, aid, ac_domain->name, NULL);
139 static int msmIsProvideAllowed(ac_domain_x *provided, sw_source_x *sw_source, const char *origin)
142 /* first check provided ac_domain attributes */
143 if (provided->sw_source == sw_source) {
144 /* allowed always if ac_domain is provided in the same sw source */
146 } else if (origin && !strcmp(origin, "current")) {
147 /* denied if ac_domain is only meant for current sw source */
150 if (origin && !strcmp(origin, "all")) {
151 /* ac_domain is allowed for all sw sources */
154 if (!origin || !strcmp(origin, "trusted")) {
155 if (strcmp(sw_source->rankkey, provided->sw_source->rankkey) < 0) {
156 /* higher ranked sw sources are allowed if ac_domain is trusted */
158 } /* else flow through to check denys and allows below */
161 return msmCheckACDomainRules(provided, sw_source, provided->sw_source);
164 static int msmSetSmackProvide(struct smack_accesses *smack_accesses, provide_x *provide, sw_source_x *sw_source)
166 ac_domain_x *ac_domain;
167 sw_source_x *current = sw_source;
170 if (!provide || (!provide->ac_domains)) return 0;
172 /* set smack rules for all sw sources */
173 LISTHEAD(current, sw_source);
174 for (; sw_source; sw_source = sw_source->next) {
175 if (!sw_source->newer) {
176 for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev) {
177 ac_domain->allowed = msmIsProvideAllowed(ac_domain, sw_source, ac_domain->origin);
178 rpmlog(RPMLOG_DEBUG, "%s ac_domain %s provided in %s for %s\n", (ac_domain->allowed ? "allowing" : "not allowing"),
179 ac_domain->name, ac_domain->sw_source->name, sw_source->name);
182 ret = msmSetSmackRules(smack_accesses, provide->ac_domains, sw_source->name);
190 static int msmSetupZypperRepo(access_x *access, sw_source_x *sw_source)
193 char path[FILENAME_MAX+1];
198 /* NOTE: Creating zypper repos manually here! */
199 /* A library call would be the correct way, but calling c++ from c */
200 /* is not nice. On the other hand, now there is no libzypp dependency. */
202 char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
203 if (!sysconfdir || !strcmp(sysconfdir, "")) {
204 rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
207 snprintf(path, sizeof(path), "%s/zypp", sysconfdir);
208 if (stat(path, &sb) == -1) {
209 rpmlog(RPMLOG_ERR, "Failed to stat %s: %s\n",
210 path, strerror(errno));
213 snprintf(path, sizeof(path), "%s/zypp/repos.d", sysconfdir);
214 if (stat(path, &sb) == -1) {
215 if (mkdir(path, 0755) == -1) {
216 rpmlog(RPMLOG_ERR, "Failed to create %s: %s\n",
217 path, strerror(errno));
221 snprintf(path, sizeof(path), "%s/zypp/repos.d/%s.repo",
222 sysconfdir, sw_source->name);
223 file = fopen(path, "w");
225 rpmlog(RPMLOG_ERR, "Failed to open %s: %s\n",
226 path, strerror(errno));
229 snprintf(data, sizeof(data),
237 sw_source->name, sw_source->name, access->data,
238 (access->type ? access->type : "NONE"));
240 if (fputs(data, file) == EOF) {
241 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
242 path, strerror(errno));
245 rpmlog(RPMLOG_DEBUG, "added zypper repository %s for sw source %s\n",
246 path, sw_source->name);
250 if (file) fclose(file);
251 msmFreePointer((void**)&sysconfdir);
256 static int msmSetSmackSWSource(struct smack_accesses *smack_accesses, sw_source_x *sw_source)
258 package_x *package, *temp;
261 if (!allpackages) return 0;
263 if (sw_source->older) {
264 ac_domain_x *ac_domain, *temp;
265 /* remove old domain rules in case of upgrade */
266 //smack_rule_set_remove_by_subject(rule_set, sw_source->name, NULL);
267 /* make sure domain's credentials point to upgraded domain */
268 HASH_ITER(hh, all_ac_domains, ac_domain, temp) {
269 if (ac_domain->sw_source == sw_source->older)
270 ac_domain->sw_source = sw_source;
274 /* iterate through all packages to create smack rules for the domain */
275 HASH_ITER(hh, allpackages, package, temp) {
276 if (sw_source->older) {
277 /* make sure domain's packages point to upgraded domain */
278 if (package->sw_source == sw_source->older)
279 package->sw_source = sw_source;
281 if (!package->newer) {
282 for (provide = package->provides; provide; provide = provide->prev) {
283 if (msmSetSmackProvide(smack_accesses, provide, package->sw_source))
291 int msmSetupSWSources(struct smack_accesses *smack_accesses, manifest_x *mfx, rpmts ts)
293 sw_source_x *sw_source;
299 ac_domain_x *ac_domain;
303 LISTHEAD(mfx->sw_sources, sw_source);
306 sw_source_x *next = sw_source->next;
307 sw_source_x *parent = sw_source->parent;
309 for (origin = sw_source->origins; origin; origin = origin->prev) {
310 for (keyinfo = origin->keyinfos; keyinfo; keyinfo = keyinfo->prev) {
311 rpmlog(RPMLOG_DEBUG, "setting keyinfo for sw source %s\n",
313 rc = rpmtsImportPubkey(ts, keyinfo->keydata, keyinfo->keylen);
314 if (rc != RPMRC_OK) {
315 rpmlog(RPMLOG_ERR, "Key import failed for sw source %s\n",
320 for (access = origin->accesses; access; access = access->prev) {
321 rpmlog(RPMLOG_DEBUG, "setting access %s for sw source %s\n",
322 access->data, sw_source->name);
323 if (origin->type && !strcmp(origin->type, "ZYPPER")) {
324 ret = msmSetupZypperRepo(access, sw_source);
327 "Failed to set access %s for sw source %s\n",
328 access->data, sw_source->name);
336 /* config processing */
337 ret = msmSetupPackages(NULL, sw_source->packages, NULL);
339 rpmlog(RPMLOG_ERR, "Setup packages failed for sw source %s\n",
345 for (allow = sw_source->allows; allow; allow = allow->hh.next) {
346 HASH_FIND(hh, all_ac_domains, allow->name, strlen(allow->name), ac_domain);
348 rpmlog(RPMLOG_DEBUG, "sw source %s allows access to ac domain %s\n",
349 sw_source->name, allow->name);
351 rpmlog(RPMLOG_WARNING, "sw source %s allows access to ac domain %s which doesn't exist\n",
352 sw_source->name, allow->name);
355 for (allow = sw_source->allowmatches; allow; allow = allow->prev)
356 rpmlog(RPMLOG_DEBUG, "sw source %s allows access to ac domain match %s\n",
357 sw_source->name, allow->match);
359 for (deny = sw_source->denys; deny; deny = deny->hh.next) {
360 HASH_FIND(hh, all_ac_domains, deny->name, strlen(deny->name), ac_domain);
362 rpmlog(RPMLOG_DEBUG, "sw source %s denies access to ac domain %s\n",
363 sw_source->name, deny->name);
365 rpmlog(RPMLOG_WARNING, "sw source %s denies access to ac domain %s which doesn't exist\n",
366 sw_source->name, deny->name);
369 for (deny = sw_source->denymatches; deny; deny = deny->prev)
370 rpmlog(RPMLOG_DEBUG, "sw source %s denies access to ac domain match %s\n",
371 sw_source->name, deny->match);
374 if (strcmp(parent->name, sw_source->name)) {
376 for (older = parent; older; older = older->next) {
377 if (!strcmp(sw_source->name, older->name)) {
378 sw_source->older = older;
379 older->newer = sw_source;
383 } else if (!parent->parent) {
384 /* root sw_source upgrade */
385 sw_source->older = parent;
386 parent->newer = sw_source;
387 sw_source->parent = NULL;
390 LISTDEL(mfx->sw_sources, sw_source); /* take out from sw sources list */
391 NODEADD(parent, sw_source); /* add to sw source tree */
394 /* set smack rules for the new/upgraded sw source */
395 ret = msmSetSmackSWSource(smack_accesses, sw_source);
397 rpmlog(RPMLOG_ERR, "Setting smack rules failed for sw source %s\n",
408 static void msmRemoveDBusConfig(package_x *package, dbus_x *dbuss)
412 for (dbus = dbuss; dbus; dbus = dbus->prev) {
413 char path[FILENAME_MAX+1];
414 snprintf(path, sizeof(path), "/etc/dbus-1/%s.d/manifest.%s.conf",
415 dbus->bus, package->name);
420 static int msmSetupDBusRule(FILE *file, const char *creds, int type, const char *service, const char *name, const char *parentType, const char *parentValue, manifest_x *mfx)
424 if (creds && *creds) {
425 // check first that it is of an allowed value
426 if (msmCheckDomainRequestOrPermit(mfx, creds) != 0) {
427 rpmlog(RPMLOG_ERR, "The label %s isn't allowed to be accessed by device security policy\n", creds);
432 snprintf(data, sizeof(data),
433 " <policy context=\"default\">\n"
434 " <deny send_destination=\"%s\"/>\n"
436 " <policy smack=\"%s\">\n"
437 " <allow send_destination=\"%s\"/>\n"
442 snprintf(data, sizeof(data),
443 " <policy context=\"default\">\n"
444 " <deny send_destination=\"%s\" send_path=\"%s\"/>\n"
445 " <deny receive_sender=\"%s\" receive_path=\"%s\"/>\n"
447 " <policy smack=\"%s\">\n"
448 " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
449 " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
451 service, name, service, name, creds,
452 service, name, service, name);
455 snprintf(data, sizeof(data),
456 " <policy context=\"default\">\n"
457 " <deny send_destination=\"%s\" send_interface=\"%s\"/>\n"
458 " <deny receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
460 " <policy smack=\"%s\">\n"
461 " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
462 " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
464 service, name, service, name, creds,
465 service, name, service, name);
468 snprintf(data, sizeof(data),
469 " <policy context=\"default\">\n"
470 " <deny send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
472 " <policy smack=\"%s\">\n"
473 " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
475 service, parentType, parentValue, name, creds,
476 service, parentType, parentValue, name);
479 snprintf(data, sizeof(data),
480 " <policy context=\"default\">\n"
481 " <deny receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
483 " <policy smack=\"%s\">\n"
484 " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
486 service, parentType, parentValue, name, creds,
487 service, parentType, parentValue, name);
495 snprintf(data, sizeof(data),
496 " <policy context=\"default\">\n"
497 " <allow send_destination=\"%s\"/>\n"
502 snprintf(data, sizeof(data),
503 " <policy context=\"default\">\n"
504 " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
505 " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
507 service, name, service, name);
510 snprintf(data, sizeof(data),
511 " <policy context=\"default\">\n"
512 " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
513 " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
515 service, name, service, name);
518 snprintf(data, sizeof(data),
519 " <policy context=\"default\">\n"
520 " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
522 service, parentType, parentValue, name);
525 snprintf(data, sizeof(data),
526 " <policy context=\"default\">\n"
527 " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
529 service, parentType, parentValue, name);
535 if (fputs(data, file) == EOF) {
536 rpmlog(RPMLOG_ERR, "Failed to write DBus rule %s: %s\n",
537 data, strerror(errno));
543 static int msmSetupDBusConfig(package_x *package, dbus_x *dbus, int phase, manifest_x *mfx)
545 char path[FILENAME_MAX+1];
549 interface_x *interface;
553 char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
554 if (!sysconfdir || !strcmp(sysconfdir, "")) {
555 rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
558 snprintf(path, sizeof(path), "%s/dbus-1/%s.d/manifest.%s.conf",
559 sysconfdir, dbus->bus, package->name);
561 file = fopen(path, phase ? "a" : "w");
563 rpmlog(RPMLOG_ERR, "Cannot open %s: %s\n", path, strerror(errno));
568 snprintf(data, sizeof(data),
569 "<!-- This configuration is automatically generated from Manifest by RPM %s security plugin -->\n"
570 "<!DOCTYPE busconfig PUBLIC \"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n"
573 if (fputs(data, file) == EOF) {
574 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
575 path, strerror(errno));
582 snprintf(data, sizeof(data),
583 " <policy context=\"default\">\n"
584 " <deny own=\"%s\"/>\n"
586 " <policy smack=\"%s\">\n"
587 " <allow own=\"%s\"/>\n"
589 dbus->name, dbus->own, dbus->name);
590 if (fputs(data, file) == EOF) {
591 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
592 path, strerror(errno));
596 if (dbus->annotation) {
597 msmSetupDBusRule(file, dbus->annotation->value, DBUS_SERVICE,
598 NULL, dbus->name, NULL, NULL, mfx);
600 for (node = dbus->nodes; node; node = node->prev) {
601 if (node->annotation) {
602 msmSetupDBusRule(file, node->annotation->value, DBUS_PATH,
603 dbus->name, node->name, NULL, NULL, mfx);
605 for (member = node->members; member; member = member->prev) {
606 if (member->annotation) {
607 msmSetupDBusRule(file, member->annotation->value, member->type,
608 dbus->name, member->name,
609 "path", node->name, mfx);
612 for (interface = node->interfaces; interface; interface = interface->prev) {
613 if (interface->annotation) {
614 msmSetupDBusRule(file, interface->annotation->value, DBUS_INTERFACE,
615 dbus->name, interface->name, NULL, NULL, mfx);
617 for (member = interface->members; member; member = member->prev) {
618 if (member->annotation) {
619 msmSetupDBusRule(file, member->annotation->value, member->type,
620 dbus->name, member->name,
621 "interface", interface->name, mfx);
629 snprintf(data, sizeof(data), "</busconfig>\n");
630 if (fputs(data, file) == EOF) {
631 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
632 path, strerror(errno));
635 rpmlog(RPMLOG_DEBUG, "wrote dbus config %s\n", path);
640 if (file) fclose(file);
641 if (ret) unlink(path);
642 msmFreePointer((void**)&sysconfdir);
647 static int msmIsRequestAllowed(manifest_x *mfx, ac_domain_x *provided)
650 if (mfx->sw_source == provided->sw_source) {
651 /* allowed always if ac domain is provided in the same sw source */
653 } else if (provided->origin && !strcmp(provided->origin, "current")) {
654 /* denied if ac domain is only meant for current sw source */
657 if (provided->origin && !strcmp(provided->origin, "all")) {
658 /* ac_domain is allowed for all sw sources */
661 if (!provided->origin || !strcmp(provided->origin, "trusted")) {
662 if (strcmp(mfx->sw_source->rankkey, provided->sw_source->rankkey) < 0) {
663 /* higher ranked sw sources are allowed if ac domain is trusted */
665 } /* else flow through to check denys and allows below */
668 return msmCheckACDomainRules(provided, mfx->sw_source, provided->sw_source);
671 static int msmCheckDomainJoinPossibility(manifest_x *mfx, ac_domain_x *defined_ac_domain)
674 char *tmp = NULL, *pch = NULL;
675 unsigned int found = 0;
677 if ((!mfx) || (!defined_ac_domain))
680 if (defined_ac_domain->type) {
681 if (strcmp(defined_ac_domain->type, "restricted") == 0) {
682 if (defined_ac_domain->plist) {
683 tmp = calloc(strlen(defined_ac_domain->plist) + 1, sizeof(char));
685 strncpy(tmp, defined_ac_domain->plist, strlen(defined_ac_domain->plist));
686 pch = strtok (tmp, ", ");
689 if (strcmp(pch, mfx->name) == 0) {
693 pch = strtok(NULL, ", ");
695 msmFreePointer((void**)&tmp);
698 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
699 rpmlog(RPMLOG_ERR, "because ac domain is marked as restricted\n");
702 } else if (strcmp(defined_ac_domain->type, "shared") == 0) {
705 // domain hasn't been marked as shared
706 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
707 rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
711 // by default ac domains are private
712 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
713 rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
720 int msmSetupRequests(manifest_x *mfx)
723 ac_domain_x *defined_ac_domain = NULL;
725 if ((!mfx) || (!mfx->request) || (!mfx->request->ac_domain))
728 HASH_FIND(hh, all_ac_domains, mfx->request->ac_domain, strlen(mfx->request->ac_domain), defined_ac_domain);
729 if (!defined_ac_domain){ // request for a undefined domain.
730 rpmlog(RPMLOG_ERR, "Request for a domain name %s that hasn't been yet defined by any package\n", mfx->request->ac_domain);
734 //now check that the package can join the requested AC domain
737 rpmlog(RPMLOG_DEBUG, "mfx->define->name %s mfx->request->ac_domain %s\n", mfx->define->name, mfx->request->ac_domain);
738 if (strcmp(mfx->define->name, mfx->request->ac_domain) == 0)
739 //ac domain is requested from the same package where it was define. This case is always allowed
743 //need to check if developer allowed other packages to join this domain
744 if (msmCheckDomainJoinPossibility(mfx, defined_ac_domain) < 0 )
747 // now checking if security policy allows to join this domain
748 if (msmIsRequestAllowed(mfx, defined_ac_domain)) {
749 rpmlog(RPMLOG_DEBUG, "Request for a domain name %s is allowed based on package sw source\n", mfx->request->ac_domain);
753 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed based on package sw source\n", mfx->request->ac_domain);
758 static int msmSetupProvides(struct smack_accesses *smack_accesses, package_x *package)
761 ac_domain_x *ac_domain;
763 for (provide = package->provides; provide; provide = provide->prev) {
764 for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev) {
765 ac_domain_x *current_d = NULL;
766 ac_domain->origin = provide->origin;
768 HASH_FIND(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), current_d);
770 if (current_d) { /* ac domain has been previously defined */
772 if (strcmp(ac_domain->pkg_name, current_d->pkg_name) == 0) { /* check that it was provided by same package */
773 HASH_DELETE(hh, all_ac_domains, current_d);
774 HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
775 current_d->newer = ac_domain;
776 ac_domain->older = current_d;
777 rpmlog(RPMLOG_DEBUG, "package %s upgraded ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
780 rpmlog(RPMLOG_ERR, "package %s can't upgrade ac domain %s previously defined in package %s\n",
781 ac_domain->pkg_name, ac_domain->name, current_d->pkg_name);
785 HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
786 rpmlog(RPMLOG_DEBUG, "package %s defined ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
789 int ret = msmSetSmackProvide(smack_accesses, provide, package->sw_source);
792 rpmlog(RPMLOG_ERR, "Failed to set smack rules for provide\n");
799 int msmSetupDBusPolicies(package_x *package, manifest_x *mfx)
802 dbus_x *session = NULL;
803 dbus_x *system = NULL;
807 for (provide = package->provides; provide; provide = provide->prev) {
808 for (dbus = provide->dbuss; dbus; dbus = dbus->prev) {
809 if (!strcmp(dbus->bus, "session")) {
810 msmSetupDBusConfig(package, dbus, session ? 1 : 0, mfx);
812 } else if (!strcmp(dbus->bus, "system")) {
813 msmSetupDBusConfig(package, dbus, system ? 1 : 0, mfx);
817 if (session) msmSetupDBusConfig(package, session, -1, mfx);
818 if (system) msmSetupDBusConfig(package, system, -1, mfx);
819 session = system = NULL;
825 static int msmCheckDomainRequestOrPermit(manifest_x *mfx, const char* domain)
828 ac_domain_x *defined_ac_domain = NULL;
831 if ((!mfx) || (!domain))
834 name = calloc(strlen(domain) + 1, sizeof(char));
835 if (!name) return -1;
836 strncpy(name, domain, strlen(domain));
837 strtok(name, ":"); // remove label name if present
838 rpmlog(RPMLOG_DEBUG, "label name %s domain name %s \n", domain, name);
840 HASH_FIND(hh, all_ac_domains, name, strlen(name), defined_ac_domain);
841 if (!defined_ac_domain) { // request or permit for an undefined domain.
842 rpmlog(RPMLOG_ERR, "A domain name %s hasn't been yet defined by any package. Can't verify if it is allowed\n", name);
843 msmFreePointer((void**)&name);
847 //now check that this ac_domain can be requested
849 if ((mfx->define) && (mfx->define->name)) {
850 rpmlog(RPMLOG_DEBUG, "mfx->define->name %s domain %s\n", mfx->define->name, name);
851 if (strcmp(mfx->define->name, name) == 0) {
852 // AC domain access is requested or permitted from the same package where it was defined.
853 // This case is always allowed
854 msmFreePointer((void**)&name);
859 // no need to check if developer allowed other packages to request/permit this domain
860 // because this isn't a request to belong to a domain, but request/permit for domain access
862 if (msmIsRequestAllowed(mfx, defined_ac_domain)) {
863 // request or permit is allowed by domain policy
864 rpmlog(RPMLOG_DEBUG, "Request/Permit to access a domain name %s is allowed based on package sw source\n", name);
865 msmFreePointer((void**)&name);
869 rpmlog(RPMLOG_ERR, "Request/Permit to access a domain name %s isn't allowed based on package sw source\n", name);
870 msmFreePointer((void**)&name);
875 int msmSetupDefine(struct smack_accesses *smack_accesses, manifest_x *mfx)
877 d_request_x *d_request;
878 d_permit_x *d_permit;
879 ac_domain_x * defined_ac_domain = NULL;
882 if ( (!mfx) || (!mfx->define) || (!mfx->define->name)) {
883 rpmlog(RPMLOG_ERR, "Failed to setup define with empty name\n");
887 /* need to check if domain hasn't been already defined by other package */
889 HASH_FIND(hh, all_ac_domains, mfx->define->name, strlen(mfx->define->name), defined_ac_domain);
890 if ((defined_ac_domain) && (defined_ac_domain->pkg_name)) { // this domain has been previously defined
891 if (strcmp(defined_ac_domain->pkg_name, mfx->name) != 0) {
892 rpmlog(RPMLOG_ERR, "Attempt to define a domain name %s that has been already defined by package %s\n",
893 mfx->define->name, defined_ac_domain->pkg_name);
899 if (mfx->define->d_requests) {
900 for (d_request = mfx->define->d_requests; d_request; d_request = d_request->prev) {
901 // first check if the current's package sw source can grant access to requested domain
902 if ( msmCheckDomainRequestOrPermit(mfx, d_request->label_name) < 0 )
904 if ( smack_accesses_add(smack_accesses, mfx->define->name, d_request->label_name, d_request->ac_type) < 0 ) {
905 rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain requests\n");
912 if (mfx->define->d_permits) {
913 for (d_permit = mfx->define->d_permits; d_permit; d_permit = d_permit->prev) {
914 // first check if the current's package sw source can grant access to permited domain
915 if ( msmCheckDomainRequestOrPermit(mfx, d_permit->label_name) < 0 )
917 if (!d_permit->to_label_name)
918 ret = smack_accesses_add(smack_accesses, d_permit->label_name, mfx->define->name, d_permit->ac_type);
920 if ( msmCheckLabelProvisioning(mfx, d_permit->to_label_name) < 0 )
922 ret = smack_accesses_add(smack_accesses, d_permit->label_name, d_permit->to_label_name, d_permit->ac_type);
925 rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain permits\n");
934 package_x *msmCreatePackage(const char *name, sw_source_x *sw_source, provide_x *provides, const char *modified)
936 if (!name) return NULL;
938 package_x *package = calloc(1, sizeof(package_x));
940 package->name = strdup(name);
941 if (!package->name) goto exit;
942 package->sw_source = sw_source;
943 package->provides = provides;
945 package->modified = strdup(modified);
946 if (!package->modified) goto exit;
952 msmFreePointer((void**)&package->name);
953 msmFreePointer((void**)&package->modified);
954 msmFreePointer((void**)&package);
959 int msmSetupSmackRules(struct smack_accesses *smack_accesses, const char* package_name, int flag, int SmackEnabled)
962 char * buffer = calloc(strlen(SMACK_RULES_PATH) + strlen(package_name) + 1, sizeof(char));
963 if (!buffer) return -1;
964 strncpy(buffer, SMACK_RULES_PATH, strlen(SMACK_RULES_PATH));
965 strncpy(buffer + strlen(SMACK_RULES_PATH), package_name, strlen(package_name));
966 rpmlog(RPMLOG_DEBUG, "smack rule file path %s, SmackEnabled %d\n", buffer, SmackEnabled);
968 if (flag == SMACK_UNINSTALL) { /* uninstallation case */
969 FILE* fd = fopen(buffer, "r");
971 rpmlog(RPMLOG_DEBUG, "uninstall case \n");
972 struct smack_accesses *old_rule_set = NULL;
973 ret = smack_accesses_new(&old_rule_set);
974 if (ret != 0) return -1;
975 ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
977 if (SmackEnabled == 1)
978 ret = smack_accesses_clear(old_rule_set); /* deletes rules from kernel */
981 smack_accesses_free(old_rule_set);
983 remove(buffer); /* delete rules file from system */
985 } else { /*installation case */
986 /* first attempt to clean previous version of rules, if exists */
987 FILE* fd = fopen(buffer, "r");
989 struct smack_accesses *old_rule_set = NULL;
990 ret = smack_accesses_new(&old_rule_set);
991 if (ret != 0) return -1;
992 ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
994 if (SmackEnabled == 1)
995 ret = smack_accesses_clear(old_rule_set); /* deletes old rules from kernel */
998 smack_accesses_free(old_rule_set);
1000 /* now write new rules to the system */
1001 fd = fopen(buffer, "w");
1003 rpmlog(RPMLOG_ERR, "Can't write smack rules\n");
1006 ret = smack_accesses_save(smack_accesses, fileno(fd));
1007 rpmlog(RPMLOG_DEBUG, "ret in installation %d\n", ret);
1009 if (SmackEnabled == 1)
1010 ret = smack_accesses_apply(smack_accesses);
1022 int msmSetupPackages(struct smack_accesses *smack_accesses, package_x *packages, sw_source_x *sw_source)
1024 package_x *package, *first = NULL;
1025 char *p_rankkey, *c_rankkey;
1026 for (package = packages; package; package = package->prev) {
1027 package_x *current_p;
1028 rpmlog(RPMLOG_DEBUG, "before HASH_FIND, package->name %s\n", package->name);
1029 HASH_FIND(hh, allpackages, package->name, strlen(package->name), current_p);
1030 rpmlog(RPMLOG_DEBUG, "after HASH_FIND\n");
1032 if (!current_p->sw_source) {
1035 p_rankkey = strdup(package->sw_source->rankkey);
1036 c_rankkey = strdup(current_p->sw_source->rankkey);
1037 p_rankkey = strtok(p_rankkey, ".");
1038 c_rankkey = strtok(c_rankkey, ".");
1039 /* this is an upgrade, remove old one from config */
1040 if ((strcmp(p_rankkey, c_rankkey) < 0) ||
1041 (strcmp(package->sw_source->name, current_p->sw_source->name) == 0)) {
1042 HASH_DELETE(hh, allpackages, current_p);
1043 rpmlog(RPMLOG_DEBUG, "sw source %s upgraded package %s previously provided in sw source %s\n",
1044 package->sw_source->name, package->name, current_p->sw_source->name);
1045 current_p->newer = package;
1046 package->older = current_p;
1048 /* upgrade from lower or similary ranked sw source is not allowed */
1049 rpmlog(RPMLOG_ERR, "sw source %s tried to upgrade package %s previously provided in sw source %s\n",
1050 package->sw_source->name, package->name, current_p->sw_source->name);
1053 msmFreePointer((void**)&p_rankkey);
1054 msmFreePointer((void**)&c_rankkey);
1057 rpmlog(RPMLOG_DEBUG, "sw source %s provided package %s\n", package->sw_source->name, package->name);
1060 rpmlog(RPMLOG_DEBUG, "before HASH_ADD_KEYPTR\n");
1061 HASH_ADD_KEYPTR(hh, allpackages, package->name, strlen(package->name), package);
1062 /* set sw source smack rules*/
1063 if ((msmSetupProvides(smack_accesses, package)) < 0 ) {
1064 msmCancelPackage(package->name);
1069 if (sw_source && packages) {
1070 /* catenate list to sw_source config */
1071 LISTCAT(sw_source->packages, first, packages);
1076 package_x *msmCheckPackage(const char *name)
1078 package_x *package = NULL;
1081 HASH_FIND(hh, allpackages, name, strlen(name), package);
1086 static void msmCancelACDomain(const char *name)
1089 ac_domain_x *domain;
1090 HASH_FIND(hh, all_ac_domains, name, strlen(name), domain);
1092 HASH_DELETE(hh, all_ac_domains, domain);
1093 if (domain->older) {
1094 /* resume previous version */
1095 HASH_ADD_KEYPTR(hh, all_ac_domains, domain->older->name, strlen(domain->older->name), domain->older);
1096 domain->older->older = domain->older->newer;
1097 domain->older->newer = NULL;
1098 domain->newer = domain->older;
1099 domain->older = NULL;
1101 /* no previous, just take this one out */
1102 domain->newer = domain;
1108 void msmCancelPackage(const char *name)
1111 ac_domain_x *ac_domain;
1115 HASH_FIND(hh, allpackages, name, strlen(name), package);
1117 HASH_DELETE(hh, allpackages, package);
1118 if (package->older) {
1119 /* resume previous version */
1120 HASH_ADD_KEYPTR(hh, allpackages, package->older->name, strlen(package->older->name), package->older);
1121 package->older->older = package->older->newer;
1122 package->older->newer = NULL;
1123 package->newer = package->older;
1124 package->older = NULL;
1126 /* no previous, just take this one out */
1127 package->newer = package;
1129 /* need to clean up the all_ac_domain list, too */
1130 for (provide = package->provides; provide; provide = provide->prev) {
1131 for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev)
1132 msmCancelACDomain(ac_domain->name);
1138 static int is_executable(const char* path, magic_t cookie)
1140 const char* buffer = NULL;
1144 if ((!path) || (!cookie))
1147 buffer = magic_file(cookie, path);
1149 rpmlog(RPMLOG_DEBUG, "buffer: %s\n", buffer);
1151 if (buffer != NULL) {
1152 ptr = strstr(buffer,"executable");
1153 if (ptr) result = 0;
1154 ptr = strstr(buffer,"ELF");
1155 if (ptr) result = 0;
1161 int msmSetFileXAttributes(manifest_x *mfx, const char* filepath, magic_t cookie)
1163 provide_x *provide = NULL;
1164 filesystem_x *filesystem = NULL;
1165 size_t len = 0, match = 0;
1166 const char *label = NULL;
1167 const char *exec_label = NULL;
1168 const char *type = NULL;
1169 const char isolatedLabel[] = SMACK_ISOLATED_LABEL;
1171 int execLabeldefined = 0;
1173 if (!filepath) return -1;
1175 package_x *package = msmCheckPackage(mfx->name);
1178 for (provide = package->provides; provide; provide = provide->prev) {
1179 for (filesystem = provide->filesystems; filesystem; filesystem = filesystem->prev) {
1180 if (!strcmp(filepath, filesystem->path)) {
1182 label = filesystem->label;
1183 exec_label = filesystem->exec_label;
1184 if (filesystem->type) type = filesystem->type;
1188 len = strlen(filesystem->path);
1189 rpmlog(RPMLOG_DEBUG, "filesystem->path: %s, length %d\n", filesystem->path, len);
1190 rpmlog(RPMLOG_DEBUG, "filesystem->path + len - 1: %s\n", filesystem->path + len - 1);
1192 if ((!strncmp(filepath, filesystem->path, len)) && (filesystem->type)) {
1193 /* partial match and the directory marked as transmutable*/
1194 label = filesystem->label;
1195 exec_label = filesystem->exec_label;
1198 if (!strncmp(filesystem->path + len - 1, "*", 1)) {
1199 if (!strncmp(filepath, filesystem->path, len - 1)) {
1200 /* partial match and the path is marked with wildcard*/
1201 label = filesystem->label;
1202 exec_label = filesystem->exec_label;
1214 execLabeldefined = 1;
1215 if ((strcmp(exec_label, "none") == 0)
1216 || (strcmp(exec_label, mfx->request->ac_domain) == 0)
1217 || (strcmp(exec_label, mfx->define->name) == 0)) {
1218 // these labels are allowed
1220 // ignore all other exec labels, because they aren't allowed for security reasons
1222 rpmlog(RPMLOG_DEBUG, "It isn't allowed to label the file with smack64label other than ac domain or \"none\" value\n");
1223 rpmlog(RPMLOG_DEBUG, "The default ac domain label will be used instead\n");
1226 if ((!label) || (!exec_label)) {
1227 /* no match, use default label of AC domain */
1228 if (mfx->request) { //AC domain is requested in manifest
1229 if (mfx->request->ac_domain) {
1230 if (!label) label = mfx->request->ac_domain;
1231 if (!exec_label) exec_label = mfx->request->ac_domain;
1233 rpmlog(RPMLOG_DEBUG, "Request for AC domain is empty. Can't identify default file label\n");
1234 rpmlog(RPMLOG_DEBUG, "File will be labelled with the label \"Isolated\"\n");
1235 if (!label) label = isolatedLabel;
1236 if (!exec_label) exec_label = isolatedLabel;
1238 } else if (mfx->define) { // AC domain defined in manifest
1239 if (mfx->define->name) {
1240 if (!label) label = mfx->define->name;
1241 if (!exec_label) exec_label = mfx->define->name;
1243 rpmlog(RPMLOG_DEBUG, "Define for AC domain is empty. Can't identify default file label\n");
1244 rpmlog(RPMLOG_DEBUG, "File will be labelled with the label \"Isolated\"\n");
1245 if (!label) label = isolatedLabel;
1246 if (!exec_label) exec_label = isolatedLabel;
1248 } else { // no request or definition of domain
1249 rpmlog(RPMLOG_DEBUG, "Both define and request sections are empty. Can't identify default file label\n");
1250 rpmlog(RPMLOG_DEBUG, "File will be labelled with the label \"Isolated\"\n");
1251 if (!label) label = isolatedLabel;
1252 if (!exec_label) exec_label = isolatedLabel;
1257 rpmlog(RPMLOG_DEBUG, "setting SMACK64 %s for %s\n", label, filepath);
1259 if (lsetxattr(filepath, SMACK64, label, strlen(label), 0) < 0 ) {
1260 rpmlog(RPMLOG_ERR, "Failed to set SMACK64 %s for %s: %s\n",
1261 label, filepath, strerror(errno));
1264 if ((is_executable(filepath, cookie)) == 0) {
1265 if ((exec_label) && (strcmp(exec_label, "none") == 0)) {
1266 // do not set SMACK64EXEC
1267 rpmlog(RPMLOG_DEBUG, "not setting SMACK64EXEC for %s as requested in manifest\n", filepath);
1269 if ((mfx->package_type && (strcmp(mfx->package_type, "application") == 0))
1270 || (execLabeldefined == 1)) {
1271 rpmlog(RPMLOG_INFO, "setting SMACK64EXEC %s for %s\n", exec_label, filepath);
1272 if (lsetxattr(filepath, SMACK64EXEC, exec_label, strlen(exec_label), 0) < 0 ) {
1273 rpmlog(RPMLOG_ERR, "Failed to set SMACK64EXEC %s for %s: %s\n",
1274 exec_label, filepath, strerror(errno));
1280 if (type) { //marked as transmutable
1281 if ((lstat(filepath, &st) != -1) && (S_ISDIR(st.st_mode))) { //check that it is a directory
1282 char at_true[] = "TRUE";
1283 rpmlog(RPMLOG_DEBUG, "setting SMACK64TRANSMUTE %s for %s\n", at_true, filepath);
1284 if ( lsetxattr(filepath, SMACK64TRANSMUTE, at_true, strlen(at_true), 0) < 0 ) {
1285 rpmlog(RPMLOG_ERR, "Failed to set SMACK64TRANSMUTE %s for %s: %s\n",
1286 at_true, filepath, strerror(errno));
1289 rpmlog(RPMLOG_DEBUG, "No setting up of transmute attr for a non-directory, path %s\n",
1301 void msmRemoveRules(struct smack_accesses *smack_accesses, manifest_x *mfx, int SmackEnabled)
1306 HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
1310 if ((mfx->define) || (mfx->sw_sources)) {
1311 /* remove smack rule file and rule set from kernel */
1312 rpmlog(RPMLOG_DEBUG, "removing smack rules for %s\n", mfx->name);
1313 msmSetupSmackRules(smack_accesses, mfx->name, SMACK_UNINSTALL, SmackEnabled);
1316 for (provide = mfx->provides; provide; provide = provide->prev) {
1317 if (provide->dbuss && !package->older)
1318 msmRemoveDBusConfig(package, provide->dbuss);
1324 void msmRemoveConfig(manifest_x *mfx)
1328 HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
1330 if (!package->older) {
1331 /* set newer to remove from config list */
1332 package->newer = package;
1333 rpmlog(RPMLOG_DEBUG, "removing package for %s\n", mfx->name);
1338 sw_source_x *msmSWSourceTreeTraversal(sw_source_x *sw_sources, int (func)(sw_source_x *, void *, void*), void *param, void* param2)
1340 sw_source_x *sw_source;
1343 LISTHEAD(sw_sources, sw_source);
1344 /* sw source tree is actually a list ordered into tree traversal path */
1345 for (; sw_source; sw_source = sw_source->next)
1346 if (!sw_source->newer)
1347 if (!(func)(sw_source, param, param2)) return sw_source;