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/types.h>
39 #include <sys/xattr.h>
42 #include "rpmio/rpmlog.h"
43 #include "rpm/rpmlib.h"
44 #include <rpm/rpmmacro.h>
45 #include <rpm/rpmts.h>
49 static ac_domain_x *all_ac_domains = NULL; /* hash of all provided ac domains */
50 static package_x *allpackages = NULL; /* hash of all installed packages */
52 static int msmCheckDomainRequestOrPermit(manifest_x *mfx, const char* domain);
54 void msmFreeInternalHashes(void)
57 HASH_CLEAR(hh,all_ac_domains);
61 HASH_CLEAR(hh,allpackages);
65 static int msmCheckACDomainRules(ac_domain_x *ac_domain,
66 sw_source_x *requested, sw_source_x *provided)
68 sw_source_x *sw_source;
70 /* go through sw source and its parents: ac domains must not match */
71 /* deny or deny wildcards and must match allow or allow wildcards */
72 /* in the whole path up to the level of the providing sw source */
74 for (sw_source = requested; sw_source->parent && sw_source->parent != sw_source; sw_source = sw_source->parent) {
77 /* check first if requested ac domain is denied */
78 HASH_FIND(hh, sw_source->denys, ac_domain->name, strlen(ac_domain->name), denied);
79 if (denied) return 0; /* matched deny */
80 for (denied = sw_source->denymatches; denied; denied = denied->prev)
81 if (!strwcmp(denied->match, ac_domain->name))
82 return 0; /* matched deny wildcard */
84 /* not denied, now check if it's in allows or allowmatches */
85 HASH_FIND(hh, sw_source->allows, ac_domain->name, strlen(ac_domain->name), allowed);
86 if (allowed) continue; /* matched allow */
87 for (allowed = sw_source->allowmatches; allowed; allowed = allowed->prev)
88 if (!strwcmp(allowed->match, ac_domain->name))
89 break; /* matched allow wildcard */
90 if (allowed) continue; /* matched allow wildcard */
92 if (strcmp(sw_source->rankkey, provided->rankkey) <= 0)
93 return 1; /* ranked higher (or same sw source), allow */
94 return 0; /* not mentioned, deny */
96 return 1; /* still here, allow for root sw source */
99 static int msmCheckLabelProvisioning(manifest_x *mfx, const char* label)
102 d_provide_x *provide = NULL;
103 define_x *define = NULL;
105 if ((mfx) && (label) && (mfx->defines)) {
106 for (define = mfx->defines; define; define = define->prev) {
107 for (provide = define->d_provides; provide; provide = provide->prev) {
108 if (strcmp(provide->label_name, label) == 0)
113 rpmlog(RPMLOG_ERR, "Label %s hasn't been provided in the manifest\n", label);
117 static int msmSetSmackRules(struct smack_accesses *smack_accesses, ac_domain_x *ac_domains, const char *aid)
119 ac_domain_x *ac_domain;
122 if (!smack_accesses) return ret;
123 for (ac_domain = ac_domains; ac_domain; ac_domain = ac_domain->prev) {
124 if (ac_domain->allowed) {
125 ret = smack_accesses_add(smack_accesses, aid, ac_domain->name, "rw");
127 rpmlog(RPMLOG_ERR, "smack_add failed for %s %s\n",
128 aid, ac_domain->name);
131 }/* else if (!ac_domain->allowed && !ac_domain->newer) {
132 // remove not allowed rule in case something has changed
133 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);
181 /* FIXME(José Bollo): I'm removing this call that has the effect to create rules having the
182 sw_source->name as subject. I'm thinking that this behaviour is not expected.
183 It is solving the bug https://bugs.tizen.org/jira/browse/PTREL-638.
186 ret = msmSetSmackRules(smack_accesses, provide->ac_domains, sw_source->name);
195 static int msmSetupZypperRepo(access_x *access, sw_source_x *sw_source)
198 char path[FILENAME_MAX+1];
203 /* NOTE: Creating zypper repos manually here! */
204 /* A library call would be the correct way, but calling c++ from c */
205 /* is not nice. On the other hand, now there is no libzypp dependency. */
207 char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
208 if (!sysconfdir || !strcmp(sysconfdir, "")) {
209 rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
212 snprintf(path, sizeof(path), "%s/zypp", sysconfdir);
213 if (stat(path, &sb) == -1) {
214 rpmlog(RPMLOG_ERR, "Failed to stat %s: %s\n",
215 path, strerror(errno));
218 snprintf(path, sizeof(path), "%s/zypp/repos.d", sysconfdir);
219 if (stat(path, &sb) == -1) {
220 if (mkdir(path, 0755) == -1) {
221 rpmlog(RPMLOG_ERR, "Failed to create %s: %s\n",
222 path, strerror(errno));
226 snprintf(path, sizeof(path), "%s/zypp/repos.d/%s.repo",
227 sysconfdir, sw_source->name);
228 file = fopen(path, "w");
230 rpmlog(RPMLOG_ERR, "Failed to open %s: %s\n",
231 path, strerror(errno));
234 snprintf(data, sizeof(data),
242 sw_source->name, sw_source->name, access->data,
243 (access->type ? access->type : "NONE"));
245 if (fputs(data, file) == EOF) {
246 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
247 path, strerror(errno));
250 rpmlog(RPMLOG_DEBUG, "added zypper repository %s for sw source %s\n",
251 path, sw_source->name);
255 if (file) fclose(file);
256 msmFreePointer((void**)&sysconfdir);
261 static int msmSetSmackSWSource(struct smack_accesses *smack_accesses, sw_source_x *sw_source)
263 package_x *package, *temp;
266 if (!allpackages) return 0;
268 if (sw_source->older) {
269 ac_domain_x *ac_domain, *temp;
270 /* remove old domain rules in case of upgrade */
271 //smack_rule_set_remove_by_subject(rule_set, sw_source->name, NULL);
272 /* make sure domain's credentials point to upgraded domain */
273 HASH_ITER(hh, all_ac_domains, ac_domain, temp) {
274 if (ac_domain->sw_source == sw_source->older)
275 ac_domain->sw_source = sw_source;
279 /* iterate through all packages to create smack rules for the domain */
280 HASH_ITER(hh, allpackages, package, temp) {
281 if (sw_source->older) {
282 /* make sure domain's packages point to upgraded domain */
283 if (package->sw_source == sw_source->older)
284 package->sw_source = sw_source;
286 if (!package->newer) {
287 for (provide = package->provides; provide; provide = provide->prev) {
288 if (msmSetSmackProvide(smack_accesses, provide, package->sw_source))
296 int msmSetupSWSources(struct smack_accesses *smack_accesses, manifest_x *mfx, rpmts ts)
298 sw_source_x *sw_source;
304 ac_domain_x *ac_domain;
308 LISTHEAD(mfx->sw_sources, sw_source);
311 sw_source_x *next = sw_source->next;
312 sw_source_x *parent = sw_source->parent;
314 for (origin = sw_source->origins; origin; origin = origin->prev) {
315 for (keyinfo = origin->keyinfos; keyinfo; keyinfo = keyinfo->prev) {
316 rpmlog(RPMLOG_DEBUG, "setting keyinfo for sw source %s\n",
318 rc = rpmtsImportPubkey(ts, keyinfo->keydata, keyinfo->keylen);
319 if (rc != RPMRC_OK) {
320 rpmlog(RPMLOG_ERR, "Key import failed for sw source %s\n",
325 for (access = origin->accesses; access; access = access->prev) {
326 rpmlog(RPMLOG_DEBUG, "setting access %s for sw source %s\n",
327 access->data, sw_source->name);
328 if (origin->type && !strcmp(origin->type, "ZYPPER")) {
329 ret = msmSetupZypperRepo(access, sw_source);
332 "Failed to set access %s for sw source %s\n",
333 access->data, sw_source->name);
340 /* config processing */
341 ret = msmSetupPackages(NULL, sw_source->packages, NULL);
343 rpmlog(RPMLOG_ERR, "Setup packages failed for sw source %s\n",
349 for (allow = sw_source->allows; allow; allow = allow->hh.next) {
350 HASH_FIND(hh, all_ac_domains, allow->name, strlen(allow->name), ac_domain);
352 rpmlog(RPMLOG_DEBUG, "sw source %s allows access to ac domain %s\n",
353 sw_source->name, allow->name);
355 rpmlog(RPMLOG_WARNING, "sw source %s allows access to ac domain %s which doesn't exist\n",
356 sw_source->name, allow->name);
359 for (allow = sw_source->allowmatches; allow; allow = allow->prev)
360 rpmlog(RPMLOG_DEBUG, "sw source %s allows access to ac domain match %s\n",
361 sw_source->name, allow->match);
363 for (deny = sw_source->denys; deny; deny = deny->hh.next) {
364 HASH_FIND(hh, all_ac_domains, deny->name, strlen(deny->name), ac_domain);
366 rpmlog(RPMLOG_DEBUG, "sw source %s denies access to ac domain %s\n",
367 sw_source->name, deny->name);
369 rpmlog(RPMLOG_WARNING, "sw source %s denies access to ac domain %s which doesn't exist\n",
370 sw_source->name, deny->name);
373 for (deny = sw_source->denymatches; deny; deny = deny->prev)
374 rpmlog(RPMLOG_DEBUG, "sw source %s denies access to ac domain match %s\n",
375 sw_source->name, deny->match);
378 if (strcmp(parent->name, sw_source->name)) {
380 for (older = parent; older; older = older->next) {
381 if (!strcmp(sw_source->name, older->name)) {
382 sw_source->older = older;
383 older->newer = sw_source;
387 } else if (!parent->parent) {
388 /* root sw_source upgrade */
389 sw_source->older = parent;
390 parent->newer = sw_source;
391 sw_source->parent = NULL;
394 LISTDEL(mfx->sw_sources, sw_source); /* take out from sw sources list */
395 NODEADD(parent, sw_source); /* add to sw source tree */
398 /* set smack rules for the new/upgraded sw source */
399 ret = msmSetSmackSWSource(smack_accesses, sw_source);
401 rpmlog(RPMLOG_ERR, "Setting smack rules failed for sw source %s\n",
412 static void msmRemoveDBusConfig(package_x *package, dbus_x *dbuss)
416 for (dbus = dbuss; dbus; dbus = dbus->prev) {
417 char path[FILENAME_MAX+1];
418 snprintf(path, sizeof(path), "/etc/dbus-1/%s.d/manifest.%s.conf",
419 dbus->bus, package->name);
424 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)
428 if (creds && *creds) {
429 // check first that it is of an allowed value
430 if (msmCheckDomainRequestOrPermit(mfx, creds) != 0) {
431 rpmlog(RPMLOG_ERR, "The label %s isn't allowed to be accessed by device security policy\n", creds);
436 snprintf(data, sizeof(data),
437 " <policy context=\"default\">\n"
438 " <deny send_destination=\"%s\"/>\n"
440 " <policy smack=\"%s\">\n"
441 " <allow send_destination=\"%s\"/>\n"
446 snprintf(data, sizeof(data),
447 " <policy context=\"default\">\n"
448 " <deny send_destination=\"%s\" send_path=\"%s\"/>\n"
449 " <deny receive_sender=\"%s\" receive_path=\"%s\"/>\n"
451 " <policy smack=\"%s\">\n"
452 " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
453 " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
455 service, name, service, name, creds,
456 service, name, service, name);
459 snprintf(data, sizeof(data),
460 " <policy context=\"default\">\n"
461 " <deny send_destination=\"%s\" send_interface=\"%s\"/>\n"
462 " <deny receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
464 " <policy smack=\"%s\">\n"
465 " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
466 " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
468 service, name, service, name, creds,
469 service, name, service, name);
472 snprintf(data, sizeof(data),
473 " <policy context=\"default\">\n"
474 " <deny send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
476 " <policy smack=\"%s\">\n"
477 " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
479 service, parentType, parentValue, name, creds,
480 service, parentType, parentValue, name);
483 snprintf(data, sizeof(data),
484 " <policy context=\"default\">\n"
485 " <deny receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
487 " <policy smack=\"%s\">\n"
488 " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
490 service, parentType, parentValue, name, creds,
491 service, parentType, parentValue, name);
499 snprintf(data, sizeof(data),
500 " <policy context=\"default\">\n"
501 " <allow send_destination=\"%s\"/>\n"
506 snprintf(data, sizeof(data),
507 " <policy context=\"default\">\n"
508 " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
509 " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
511 service, name, service, name);
514 snprintf(data, sizeof(data),
515 " <policy context=\"default\">\n"
516 " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
517 " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
519 service, name, service, name);
522 snprintf(data, sizeof(data),
523 " <policy context=\"default\">\n"
524 " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
526 service, parentType, parentValue, name);
529 snprintf(data, sizeof(data),
530 " <policy context=\"default\">\n"
531 " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
533 service, parentType, parentValue, name);
539 if (fputs(data, file) == EOF) {
540 rpmlog(RPMLOG_ERR, "Failed to write DBus rule %s: %s\n",
541 data, strerror(errno));
547 static int msmSetupDBusConfig(package_x *package, dbus_x *dbus, int phase, manifest_x *mfx)
549 char path[FILENAME_MAX+1];
553 interface_x *interface;
557 char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
558 if (!sysconfdir || !strcmp(sysconfdir, "")) {
559 rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
563 snprintf(path, sizeof(path), "%s/dbus-1/%s.d/manifest.%s.conf",
564 sysconfdir, dbus->bus, package->name);
566 file = fopen(path, phase ? "a" : "w");
568 rpmlog(RPMLOG_ERR, "Cannot open %s: %s\n", path, strerror(errno));
574 snprintf(data, sizeof(data),
575 "<!-- This configuration is automatically generated from Manifest by RPM %s security plugin -->\n"
576 "<!DOCTYPE busconfig PUBLIC \"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n"
579 if (fputs(data, file) == EOF) {
580 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
581 path, strerror(errno));
589 snprintf(data, sizeof(data),
590 " <policy context=\"default\">\n"
591 " <deny own=\"%s\"/>\n"
593 " <policy smack=\"%s\">\n"
594 " <allow own=\"%s\"/>\n"
596 dbus->name, dbus->own, dbus->name);
597 if (fputs(data, file) == EOF) {
598 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
599 path, strerror(errno));
604 if (dbus->annotation) {
605 msmSetupDBusRule(file, dbus->annotation->value, DBUS_SERVICE,
606 NULL, dbus->name, NULL, NULL, mfx);
608 for (node = dbus->nodes; node; node = node->prev) {
609 if (node->annotation) {
610 ret = msmSetupDBusRule(file, node->annotation->value, DBUS_PATH,
611 dbus->name, node->name, NULL, NULL, mfx);
612 if (ret < 0) goto exit;
614 for (member = node->members; member; member = member->prev) {
615 if (member->annotation) {
616 ret = msmSetupDBusRule(file, member->annotation->value, member->type,
617 dbus->name, member->name,
618 "path", node->name, mfx);
619 if (ret < 0) goto exit;
622 for (interface = node->interfaces; interface; interface = interface->prev) {
623 if (interface->annotation) {
624 ret = msmSetupDBusRule(file, interface->annotation->value, DBUS_INTERFACE,
625 dbus->name, interface->name, NULL, NULL, mfx);
626 if (ret < 0) goto exit;
628 for (member = interface->members; member; member = member->prev) {
629 if (member->annotation) {
630 ret = msmSetupDBusRule(file, member->annotation->value, member->type,
631 dbus->name, member->name,
632 "interface", interface->name, mfx);
633 if (ret < 0) goto exit;
641 snprintf(data, sizeof(data), "</busconfig>\n");
642 if (fputs(data, file) == EOF) {
643 rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
644 path, strerror(errno));
648 rpmlog(RPMLOG_DEBUG, "wrote dbus config %s\n", path);
653 if (file) fclose(file);
654 if (ret < 0) unlink(path);
655 msmFreePointer((void**)&sysconfdir);
659 static int msmIsRequestAllowed(manifest_x *mfx, ac_domain_x *provided)
661 if (mfx->sw_source == provided->sw_source) {
662 /* allowed always if ac domain is provided in the same sw source */
664 } else if (provided->origin && !strcmp(provided->origin, "current")) {
665 /* denied if ac domain is only meant for current sw source */
668 if (provided->origin && !strcmp(provided->origin, "all")) {
669 /* ac_domain is allowed for all sw sources */
672 if (!provided->origin || !strcmp(provided->origin, "trusted")) {
673 if (strcmp(mfx->sw_source->rankkey, provided->sw_source->rankkey) < 0) {
674 /* higher ranked sw sources are allowed if ac domain is trusted */
676 } /* else flow through to check denys and allows below */
679 return msmCheckACDomainRules(provided, mfx->sw_source, provided->sw_source);
682 static int msmCheckDomainJoinPossibility(manifest_x *mfx, ac_domain_x *defined_ac_domain)
685 char *tmp = NULL, *pch = NULL;
686 unsigned int found = 0;
688 if ((!mfx) || (!defined_ac_domain))
691 if (defined_ac_domain->type) {
692 if (strcmp(defined_ac_domain->type, "restricted") == 0) {
693 if (defined_ac_domain->plist) {
694 tmp = calloc(strlen(defined_ac_domain->plist) + 1, sizeof(char));
696 strncpy(tmp, defined_ac_domain->plist, strlen(defined_ac_domain->plist));
697 pch = strtok (tmp, ", ");
698 while (pch != NULL) {
699 if (strcmp(pch, mfx->name) == 0) {
703 pch = strtok(NULL, ", ");
705 msmFreePointer((void**)&tmp);
708 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
709 rpmlog(RPMLOG_ERR, "because ac domain is marked as restricted\n");
712 } else if (strcmp(defined_ac_domain->type, "shared") == 0) {
715 // domain hasn't been marked as shared
716 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
717 rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
721 // by default ac domains are private
722 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
723 rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
729 int msmSetupRequests(manifest_x *mfx)
731 ac_domain_x *defined_ac_domain = NULL;
732 define_x *define = NULL;
734 if ((!mfx) || (!mfx->request) || (!mfx->request->ac_domain))
737 HASH_FIND(hh, all_ac_domains, mfx->request->ac_domain, strlen(mfx->request->ac_domain), defined_ac_domain);
738 if (!defined_ac_domain){ // request for a undefined domain.
739 rpmlog(RPMLOG_ERR, "Request for a domain name %s that hasn't been yet defined by any package\n", mfx->request->ac_domain);
742 //now check that the package can join the requested AC domain
744 LISTHEAD(mfx->defines, define);
746 rpmlog(RPMLOG_DEBUG, "define->name %s mfx->request->ac_domain %s\n", define->name, mfx->request->ac_domain);
747 if (strcmp(define->name, mfx->request->ac_domain) == 0)
748 //ac domain is requested from the same package where it was define. This case is always allowed
750 define = define->next;
753 //need to check if developer allowed other packages to join this domain
754 if (msmCheckDomainJoinPossibility(mfx, defined_ac_domain) < 0) {
757 // now checking if security policy allows to join this domain
758 if (msmIsRequestAllowed(mfx, defined_ac_domain)) {
759 rpmlog(RPMLOG_DEBUG, "Request for a domain name %s is allowed based on package sw source\n", mfx->request->ac_domain);
762 rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed based on package sw source\n", mfx->request->ac_domain);
767 static int msmSetupProvides(struct smack_accesses *smack_accesses, package_x *package)
770 ac_domain_x *ac_domain;
772 for (provide = package->provides; provide; provide = provide->prev) {
773 for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev) {
774 ac_domain_x *current_d = NULL;
775 ac_domain->origin = provide->origin;
777 HASH_FIND(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), current_d);
778 if (current_d) { /* ac domain has been previously defined */
779 if (strcmp(ac_domain->pkg_name, current_d->pkg_name) == 0) { /* check that it was provided by same package */
780 HASH_DELETE(hh, all_ac_domains, current_d);
781 HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
782 current_d->newer = ac_domain;
783 ac_domain->older = current_d;
784 rpmlog(RPMLOG_DEBUG, "package %s upgraded ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
786 rpmlog(RPMLOG_ERR, "package %s can't upgrade ac domain %s previously defined in package %s\n",
787 ac_domain->pkg_name, ac_domain->name, current_d->pkg_name);
791 HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
792 rpmlog(RPMLOG_DEBUG, "package %s defined ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
795 int ret = msmSetSmackProvide(smack_accesses, provide, package->sw_source);
797 rpmlog(RPMLOG_ERR, "Failed to set smack rules for provide\n");
804 int msmSetupDBusPolicies(package_x *package, manifest_x *mfx)
806 dbus_x *session = NULL;
807 dbus_x *system = NULL;
812 for (provide = package->provides; provide; provide = provide->prev) {
813 for (dbus = provide->dbuss; dbus; dbus = dbus->prev) {
814 if (!strcmp(dbus->bus, "session")) {
815 ret = msmSetupDBusConfig(package, dbus, session ? 1 : 0, mfx);
817 } else if (!strcmp(dbus->bus, "system")) {
818 ret = msmSetupDBusConfig(package, dbus, system ? 1 : 0, mfx);
821 if (ret < 0) return ret;
823 if (session) ret = msmSetupDBusConfig(package, session, -1, mfx);
824 if (system) ret = msmSetupDBusConfig(package, system, -1, mfx);
825 session = system = NULL;
830 static int msmCheckDomainRequestOrPermit(manifest_x *mfx, const char* domain)
832 ac_domain_x *defined_ac_domain = NULL;
833 define_x *define = NULL;
836 if ((!mfx) || (!domain))
839 name = calloc(strlen(domain) + 1, sizeof(char));
840 if (!name) return -1;
841 strncpy(name, domain, strlen(domain));
842 strtok(name, ":"); // remove label name if present
843 rpmlog(RPMLOG_DEBUG, "label name %s domain name %s \n", domain, name);
845 HASH_FIND(hh, all_ac_domains, name, strlen(name), defined_ac_domain);
846 if (!defined_ac_domain) // request or permit for an undefined domain.
847 // FIXME: maybe this should be changed to a command-line option that
848 // would be used during the image build?
849 rpmlog(RPMLOG_WARNING, "The domain '%s' has not been yet defined by "\
850 "any package\n", domain);
852 //now check that this ac_domain can be requested
854 LISTHEAD(mfx->defines, define);
856 rpmlog(RPMLOG_DEBUG, "define->name %s domain %s\n", define->name,
858 if (strcmp(define->name, name) == 0) {
859 // AC domain access is requested or permitted from the same
860 // package where it was defined. This case is always allowed.
861 msmFreePointer((void**)&name);
864 define = define->next;
868 // no need to check if developer allowed other packages to
869 // request/permit this domain because this isn't a request to
870 // belong to a domain, but request/permit for domain access
871 if (!defined_ac_domain)
872 // FIXME: maybe this should be changed to a command-line option that
873 // would be used during the image build?
874 rpmlog(RPMLOG_WARNING, "Request/Permit to access the domain '%s' is "\
876 else if (msmIsRequestAllowed(mfx, defined_ac_domain))
877 // request or permit is allowed by domain policy
878 rpmlog(RPMLOG_DEBUG, "Request/Permit to access the domain '%s' is "\
879 "allowed based on package SW source\n", name);
881 rpmlog(RPMLOG_ERR, "Request/Permit access the domain '%s' is not "\
882 "allowed based on package SW source\n", name);
883 msmFreePointer((void**)&name);
887 msmFreePointer((void**)&name);
891 int msmSetupDefines(struct smack_accesses *smack_accesses, manifest_x *mfx)
893 d_request_x *d_request;
895 d_permit_x *d_permit;
896 ac_domain_x * defined_ac_domain = NULL;
899 if ( (!mfx) || (!mfx->defines)) {
900 rpmlog(RPMLOG_ERR, "Failed to setup define\n");
904 LISTHEAD(mfx->defines, define);
907 define_x *next = define->next;
909 rpmlog(RPMLOG_ERR, "Attempt to define a domain with empty name. Abort\n");
912 /* need to check if domain hasn't been already defined by other package */
914 HASH_FIND(hh, all_ac_domains, define->name, strlen(define->name), defined_ac_domain);
915 if ((defined_ac_domain) && (defined_ac_domain->pkg_name)) { // this domain has been previously defined
916 if (strcmp(defined_ac_domain->pkg_name, mfx->name) != 0) {
917 rpmlog(RPMLOG_ERR, "Attempt to define a domain name %s that has been already defined by package %s\n",
918 define->name, defined_ac_domain->pkg_name);
923 if (define->d_requests) {
924 for (d_request = define->d_requests; d_request; d_request = d_request->prev) {
925 // first check if the current's package sw source can grant access to requested domain
926 if (msmCheckDomainRequestOrPermit(mfx, d_request->label_name) < 0) {
929 if (smack_accesses_add(smack_accesses, define->name, d_request->label_name, d_request->ac_type) < 0) {
930 rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain requests\n");
936 if (define->d_permits) {
937 for (d_permit = define->d_permits; d_permit; d_permit = d_permit->prev) {
938 // first check if the current's package sw source can grant access to permited domain
939 if (msmCheckDomainRequestOrPermit(mfx, d_permit->label_name) < 0) {
942 if (!d_permit->to_label_name)
943 ret = smack_accesses_add(smack_accesses, d_permit->label_name, define->name, d_permit->ac_type);
945 if (msmCheckLabelProvisioning(mfx, d_permit->to_label_name) < 0) {
948 ret = smack_accesses_add(smack_accesses, d_permit->label_name, d_permit->to_label_name, d_permit->ac_type);
951 rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain permits\n");
963 package_x *msmCreatePackage(const char *name, sw_source_x *sw_source, provide_x *provides, const char *modified)
965 if (!name) return NULL;
967 package_x *package = calloc(1, sizeof(package_x));
969 package->name = strdup(name);
970 if (!package->name) goto exit;
971 package->sw_source = sw_source;
972 package->provides = provides;
974 package->modified = strdup(modified);
975 if (!package->modified) goto exit;
981 msmFreePointer((void**)&package->name);
982 msmFreePointer((void**)&package->modified);
983 msmFreePointer((void**)&package);
988 int msmSetupSmackRules(struct smack_accesses *smack_accesses, const char* package_name, int flag, int SmackEnabled)
992 char * buffer = calloc(strlen(SMACK_RULES_PATH) + strlen(package_name) + 1, sizeof(char));
993 if (!buffer) return -1;
994 strncpy(buffer, SMACK_RULES_PATH, strlen(SMACK_RULES_PATH));
995 strncpy(buffer + strlen(SMACK_RULES_PATH), package_name, strlen(package_name));
996 rpmlog(RPMLOG_DEBUG, "smack rule file path %s, SmackEnabled %d\n", buffer, SmackEnabled);
998 if (flag == SMACK_UNINSTALL) { /* uninstallation case */
999 FILE* fd = fopen(buffer, "r");
1001 rpmlog(RPMLOG_DEBUG, "uninstall case \n");
1002 struct smack_accesses *old_rule_set = NULL;
1003 ret = smack_accesses_new(&old_rule_set);
1004 if (ret != 0) return -1;
1005 ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
1007 if (SmackEnabled == 1)
1008 ret = smack_accesses_clear(old_rule_set); /* deletes rules from kernel */
1010 smack_accesses_free(old_rule_set);
1012 remove(buffer); /* delete rules file from system */
1014 } else { /*installation case */
1015 /* first attempt to clean previous version of rules, if exists */
1016 FILE* fd = fopen(buffer, "r");
1018 struct smack_accesses *old_rule_set = NULL;
1019 ret = smack_accesses_new(&old_rule_set);
1020 if (ret != 0) return -1;
1021 ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
1023 if (SmackEnabled == 1)
1024 ret = smack_accesses_clear(old_rule_set); /* deletes old rules from kernel */
1027 smack_accesses_free(old_rule_set);
1029 /* now write new rules to the system */
1030 fd = fopen(buffer, "w");
1032 rpmlog(RPMLOG_ERR, "Can't write smack rules\n");
1035 ret = smack_accesses_save(smack_accesses, fileno(fd));
1036 rpmlog(RPMLOG_DEBUG, "ret in installation %d\n", ret);
1039 if (SmackEnabled == 1)
1040 ret = smack_accesses_apply(smack_accesses);
1044 unlink(buffer); /* status not checked because it dont care */
1053 int msmSetupPackages(struct smack_accesses *smack_accesses, package_x *packages, sw_source_x *sw_source)
1055 package_x *package, *first = NULL;
1056 char *p_rankkey, *c_rankkey;
1057 for (package = packages; package; package = package->prev) {
1058 package_x *current_p;
1059 rpmlog(RPMLOG_DEBUG, "before HASH_FIND, package->name %s\n", package->name);
1060 HASH_FIND(hh, allpackages, package->name, strlen(package->name), current_p);
1062 if (!current_p->sw_source) {
1065 p_rankkey = strdup(package->sw_source->rankkey);
1066 c_rankkey = strdup(current_p->sw_source->rankkey);
1067 p_rankkey = strtok(p_rankkey, ".");
1068 c_rankkey = strtok(c_rankkey, ".");
1069 /* this is an upgrade, remove old one from config */
1070 if ((strcmp(p_rankkey, c_rankkey) < 0) ||
1071 (strcmp(package->sw_source->name, current_p->sw_source->name) == 0)) {
1072 HASH_DELETE(hh, allpackages, current_p);
1073 rpmlog(RPMLOG_DEBUG, "sw source %s upgraded package %s previously provided in sw source %s\n",
1074 package->sw_source->name, package->name, current_p->sw_source->name);
1075 current_p->newer = package;
1076 package->older = current_p;
1078 /* upgrade from lower or similary ranked sw source is not allowed */
1079 rpmlog(RPMLOG_ERR, "sw source %s tried to upgrade package %s previously provided in sw source %s\n",
1080 package->sw_source->name, package->name, current_p->sw_source->name);
1083 msmFreePointer((void**)&p_rankkey);
1084 msmFreePointer((void**)&c_rankkey);
1087 rpmlog(RPMLOG_DEBUG, "sw source %s provided package %s\n", package->sw_source->name, package->name);
1090 rpmlog(RPMLOG_DEBUG, "before HASH_ADD_KEYPTR\n");
1091 HASH_ADD_KEYPTR(hh, allpackages, package->name, strlen(package->name), package);
1092 /* set sw source smack rules*/
1093 if ((msmSetupProvides(smack_accesses, package)) < 0 ) {
1094 msmCancelPackage(package->name);
1099 if (sw_source && packages) {
1100 /* catenate list to sw_source config */
1101 LISTCAT(sw_source->packages, first, packages);
1106 package_x *msmCheckPackage(const char *name)
1108 package_x *package = NULL;
1110 HASH_FIND(hh, allpackages, name, strlen(name), package);
1114 static void msmCancelACDomain(const char *name)
1117 ac_domain_x *domain;
1118 HASH_FIND(hh, all_ac_domains, name, strlen(name), domain);
1120 HASH_DELETE(hh, all_ac_domains, domain);
1121 if (domain->older) {
1122 /* resume previous version */
1123 HASH_ADD_KEYPTR(hh, all_ac_domains, domain->older->name, strlen(domain->older->name), domain->older);
1124 domain->older->older = domain->older->newer;
1125 domain->older->newer = NULL;
1126 domain->newer = domain->older;
1127 domain->older = NULL;
1129 /* no previous, just take this one out */
1130 domain->newer = domain;
1136 void msmCancelPackage(const char *name)
1139 ac_domain_x *ac_domain;
1143 HASH_FIND(hh, allpackages, name, strlen(name), package);
1145 HASH_DELETE(hh, allpackages, package);
1146 if (package->older) {
1147 /* resume previous version */
1148 HASH_ADD_KEYPTR(hh, allpackages, package->older->name, strlen(package->older->name), package->older);
1149 package->older->older = package->older->newer;
1150 package->older->newer = NULL;
1151 package->newer = package->older;
1152 package->older = NULL;
1154 /* no previous, just take this one out */
1155 package->newer = package;
1157 /* need to clean up the all_ac_domain list, too */
1158 for (provide = package->provides; provide; provide = provide->prev) {
1159 for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev)
1160 msmCancelACDomain(ac_domain->name);
1166 static int is_executable(const char* path, magic_t cookie)
1168 const char* buffer = NULL;
1172 if ((!path) || (!cookie))
1175 buffer = magic_file(cookie, path);
1176 rpmlog(RPMLOG_DEBUG, "buffer: %s\n", buffer);
1178 if (buffer != NULL) {
1179 ptr = strstr(buffer,"executable");
1180 if (ptr) result = 0;
1181 ptr = strstr(buffer,"ELF");
1182 if (ptr) result = 0;
1188 int msmSetFileXAttributes(manifest_x *mfx, const char* filepath, magic_t cookie)
1190 provide_x *provide = NULL;
1191 filesystem_x *filesystem = NULL;
1192 size_t len = 0, match = 0;
1193 const char *label = NULL;
1194 const char *exec_label = NULL;
1195 const char *type = NULL;
1196 const char isolatedLabel[] = SMACK_ISOLATED_LABEL;
1198 int execLabeldefined = 0;
1200 if (!filepath) return -1;
1202 package_x *package = msmCheckPackage(mfx->name);
1203 if (!package) return -1;
1204 for (provide = package->provides; provide; provide = provide->prev) {
1205 for (filesystem = provide->filesystems; filesystem; filesystem = filesystem->prev) {
1206 if (!strcmp(filepath, filesystem->path)) {
1208 label = filesystem->label;
1209 exec_label = filesystem->exec_label;
1210 if (filesystem->type) type = filesystem->type;
1213 len = strlen(filesystem->path);
1214 rpmlog(RPMLOG_DEBUG, "filepath: %s, filesystem->type %s\n", filepath, filesystem->type);
1215 //rpmlog(RPMLOG_DEBUG, "filesystem->path: %s, length %d, match %d\n", filesystem->path, len, match);
1216 //rpmlog(RPMLOG_DEBUG, "filesystem->path + len - 1: %s\n", filesystem->path + len - 1);
1218 if ((!strncmp(filepath, filesystem->path, len)) && (filesystem->type)) {
1219 /* partial match and the directory marked as transmutable*/
1220 label = filesystem->label;
1221 exec_label = filesystem->exec_label;
1224 if (!strncmp(filesystem->path + len - 1, "*", 1)) {
1225 if (!strncmp(filepath, filesystem->path, len - 1)) {
1226 /* partial match and the path is marked with wildcard*/
1227 label = filesystem->label;
1228 exec_label = filesystem->exec_label;
1240 execLabeldefined = 1;
1242 if ((strcmp(exec_label, "none") == 0)
1243 || ( (mfx->request) && (mfx->request->ac_domain) && (strcmp(exec_label, mfx->request->ac_domain) == 0))) {
1244 // these labels are allowed
1246 // ignore all other exec labels, because they aren't allowed for security reasons
1248 rpmlog(RPMLOG_DEBUG, "It isn't allowed to label the file with smack64label other than requested ac domain or \"none\" value\n");
1249 rpmlog(RPMLOG_DEBUG, "The default ac domain label will be used instead\n");
1253 if ((!label) || (!exec_label)) {
1254 /* no match, use default label of AC domain */
1255 if (mfx->request) { //AC domain is requested in manifest
1256 if (mfx->request->ac_domain) {
1257 if (!label) label = mfx->request->ac_domain;
1258 if (!exec_label) exec_label = mfx->request->ac_domain;
1260 rpmlog(RPMLOG_DEBUG, "Request for AC domain is empty. Can't identify default file label\n");
1261 rpmlog(RPMLOG_DEBUG, "File will be labelled with the label \"Isolated\"\n");
1262 if (!label) label = isolatedLabel;
1263 if (!exec_label) exec_label = isolatedLabel;
1265 } else { // no request of domain
1266 rpmlog(RPMLOG_DEBUG, "The request section is missing. Can't identify default file label\n");
1267 rpmlog(RPMLOG_DEBUG, "File will be labelled with the label \"Isolated\"\n");
1268 if (!label) label = isolatedLabel;
1269 if (!exec_label) exec_label = isolatedLabel;
1273 rpmlog(RPMLOG_DEBUG, "setting SMACK64 %s for %s\n", label, filepath);
1274 if (lsetxattr(filepath, SMACK64, label, strlen(label), 0) < 0 ) {
1275 rpmlog(RPMLOG_ERR, "Failed to set SMACK64 %s for %s: %s\n",
1276 label, filepath, strerror(errno));
1278 if ((is_executable(filepath, cookie)) == 0) {
1279 if ((exec_label) && (strcmp(exec_label, "none") == 0)) {
1280 // do not set SMACK64EXEC
1281 rpmlog(RPMLOG_DEBUG, "not setting SMACK64EXEC for %s as requested in manifest\n", filepath);
1283 if ((mfx->package_type && (strcmp(mfx->package_type, "application") == 0))
1284 || (execLabeldefined == 1)) {
1285 rpmlog(RPMLOG_INFO, "setting SMACK64EXEC %s for %s\n", exec_label, filepath);
1286 if (lsetxattr(filepath, SMACK64EXEC, exec_label, strlen(exec_label), 0) < 0 ) {
1287 rpmlog(RPMLOG_ERR, "Failed to set SMACK64EXEC %s for %s: %s\n",
1288 exec_label, filepath, strerror(errno));
1293 if (type) { //marked as transmutable
1294 if ((lstat(filepath, &st) != -1) && (S_ISDIR(st.st_mode))) { //check that it is a directory
1295 char at_true[] = "TRUE";
1296 rpmlog(RPMLOG_DEBUG, "setting SMACK64TRANSMUTE %s for %s\n", at_true, filepath);
1297 if (lsetxattr(filepath, SMACK64TRANSMUTE, at_true, strlen(at_true), 0) < 0) {
1298 rpmlog(RPMLOG_ERR, "Failed to set SMACK64TRANSMUTE %s for %s: %s\n",
1299 at_true, filepath, strerror(errno));
1302 rpmlog(RPMLOG_DEBUG, "No setting up of transmute attr for a non-directory, path %s\n", filepath);
1308 void msmRemoveRules(struct smack_accesses *smack_accesses, manifest_x *mfx, int SmackEnabled)
1313 HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
1317 if ((mfx->defines) || (mfx->sw_sources)) {
1318 /* remove smack rule file and rule set from kernel */
1319 rpmlog(RPMLOG_DEBUG, "removing smack rules for %s\n", mfx->name);
1320 msmSetupSmackRules(smack_accesses, mfx->name, SMACK_UNINSTALL, SmackEnabled);
1322 for (provide = mfx->provides; provide; provide = provide->prev) {
1323 if (provide->dbuss && !package->older)
1324 msmRemoveDBusConfig(package, provide->dbuss);
1328 void msmRemoveConfig(manifest_x *mfx)
1332 HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
1334 if (!package->older) {
1335 /* set newer to remove from config list */
1336 package->newer = package;
1337 rpmlog(RPMLOG_DEBUG, "removing package for %s\n", mfx->name);
1342 sw_source_x *msmSWSourceTreeTraversal(sw_source_x *sw_sources, int (func)(sw_source_x *, void *, void*), void *param, void* param2)
1344 sw_source_x *sw_source;
1347 LISTHEAD(sw_sources, sw_source);
1348 /* sw source tree is actually a list ordered into tree traversal path */
1349 for (; sw_source; sw_source = sw_source->next)
1350 if (!sw_source->newer)
1351 if (!(func)(sw_source, param, param2)) return sw_source;