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
30 #include <sys/types.h>
33 #include <sys/prctl.h>
34 #include <sys/capability.h>
37 #include <rpm/rpmfileutil.h>
38 #include <rpm/rpmpgp.h>
39 #include <rpm/rpmkeyring.h>
40 #include <rpm/rpmdb.h>
41 #include <lib/rpmts_internal.h>
43 #include "rpmio/rpmbase64.h"
44 #include "rpmio/rpmlog.h"
45 #include "rpm/rpmfileutil.h"
47 #include "lib/rpmfs.h"
48 #include "lib/rpmplugin.h"
49 #include "lib/rpmte_internal.h"
53 typedef struct fileconflict {
56 sw_source_x *sw_source;
60 typedef struct packagecontext {
61 char *data; /*!< base64 manifest data */
62 manifest_x *mfx; /*!< parsed manifest data */
63 rpmte te; /*!< related te */
64 struct packagecontext *next; /*!< next in linked list */
65 struct smack_accesses *smack_accesses; /*!< handle to smack_accesses */
68 static rpmts ts = NULL; /* rpm transaction */
69 static manifest_x *dsp = NULL; /* pointer to device security policy file */
70 static packagecontext *context = NULL;
71 static sw_source_x *current = NULL;
72 static packagecontext *contextsHead = NULL;
73 static packagecontext *contextsTail = NULL;
74 static fileconflict *allfileconflicts = NULL;
75 static char* ownSmackLabel = NULL; /* rpm smack security context */
76 static int SmackEnabled = 0; /* indicates if Smack is enabled in kernel or not */
77 static magic_t cookie = NULL;
78 static int package_created = 0;
80 /* Support functions */
83 * Frees the given pointer and sets it to NULL
84 * @param ptr address of pointer to be freed
87 static packagecontext *msmFree(packagecontext *ctx)
90 packagecontext *next = ctx->next;
91 msmFreePointer((void**)&ctx->data);
92 ctx->mfx = msmFreeManifestXml(ctx->mfx);
93 if (ctx->smack_accesses) smack_accesses_free(ctx->smack_accesses);
94 msmFreePointer((void**)&ctx);
101 * Frees the given pointer and sets it to NULL
102 * @param ptr address of pointer to be freed
105 void msmFreePointer(void** ptr)
114 * Creates a new package context
115 * @param te rpm te struct
116 * @return created package context
118 static packagecontext *msmNew(rpmte te)
123 packagecontext *ctx = NULL;
124 const char *sw_source = NULL;
131 ctx = xcalloc(1, sizeof(*ctx));
137 if (!headerIsEntry(h, RPMTAG_SECMANIFEST)) {
141 if (!headerGet(h, RPMTAG_SECMANIFEST, &msm, HEADERGET_MINMEM)) {
145 count = rpmtdCount(&msm);
150 ctx->data = xstrdup(rpmtdNextString(&msm));
151 rpmlog(RPMLOG_DEBUG, "%s manifest b64 data: %.40s...\n",
152 rpmteN(ctx->te), ctx->data);
156 if (rpmteType(ctx->te) == TR_ADDED) {
157 /* Save sw_source name into database, we need it when package */
158 /* is removed because signature verify is not called then. */
159 if (current) sw_source = current->name;
160 if (!sw_source || !headerPutString(h, RPMTAG_SECSWSOURCE, sw_source)) {
161 rpmlog(RPMLOG_ERR, "Failed to save sw source for %s, sw_source: %s\n",
162 rpmteN(ctx->te), sw_source);
163 msmFreePointer((void**)&ctx->data);
164 msmFreePointer((void**)&ctx);
173 * Creates and adds a te package context to a context list
174 * @param te rpm te struct
175 * @return created and added package context
177 static packagecontext *msmAddTE(rpmte te)
179 packagecontext *ctx = msmNew(te);
181 /* add the new policy to the list */
186 if (rpmteType(te) == TR_ADDED) {
187 /* add to the end of the list */
188 contextsTail->next = ctx;
191 /* add to the beginning of the list */
192 ctx->next = contextsHead;
200 /* -------------------- */
202 /* Implementation of hooks */
203 /* Hooks are listed in the call sequence inside rpm code */
205 rpmRC msm_plugin_init(rpmPlugin plugin, rpmts _ts)
212 if (msmLoadDeviceSecurityPolicy(ts->rootDir, &dsp) == RPMRC_FAIL)
218 /* smackfs path doesn't need to be prefixed with ts->rootDir
219 since it is only used for loading rules to kernel and libsmack will load it
220 by itself to the correct path */
222 if (smack_smackfs_path() == NULL) {
223 rpmlog(RPMLOG_INFO, "Smackfs isn't mounted at %s. Going to the image build mode. \n", smack_smackfs_path());
224 ownSmackLabel = strdup("_");
227 /* check its own security context and store it for the case when packages without manifest will be installed */
228 int res = smack_new_label_from_self(&ownSmackLabel);
231 rpmlog(RPMLOG_ERR, "Failed to obtain rpm security context\n");
236 rpmlog(RPMLOG_DEBUG, "rpm security context: %s\n", ownSmackLabel);
238 if (msmSetupSmackRulesDir(ts->rootDir) == RPMRC_FAIL)
241 cookie = magic_open(0);
245 if (magic_load(cookie, NULL) != 0) {
246 rpmlog(RPMLOG_ERR, "cannot load magic database - %s\n", magic_error(cookie));
255 rpmRC msm_plugin_fsm_file_conflict(rpmts ts, char* path,
256 Header oldHeader, rpmfi oldFi,
260 if ((!path) || (!dsp))
263 rpmlog(RPMLOG_DEBUG, "FILE_CONFLICT_FUNC hook path %s\n",path);
265 /* uncomment this code if you wish that plugin obeys --replacefiles rpm flag
266 if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
270 const char *sw_source_name = headerGetString(oldHeader, RPMTAG_SECSWSOURCE);
271 const char *pkg_name = headerGetString(oldHeader, RPMTAG_NAME);
272 if (!sw_source_name || !pkg_name) {
273 return rpmrc; /* no sw source or package name - abnormal state */
276 sw_source_x *sw_source = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)sw_source_name, NULL);
278 return rpmrc; /* no old sw_source - abnormal state */
280 HASH_FIND(hh, allfileconflicts, path, strlen(path), fc);
282 /* Add new file conflict into hash */
283 fc = xcalloc(1, sizeof(*fc));
284 if (!fc) return RPMRC_FAIL;
286 fc->sw_source = sw_source;
287 fc->pkg_name = strdup(pkg_name);
288 if (!fc->pkg_name) return RPMRC_FAIL;
289 HASH_ADD_KEYPTR(hh, allfileconflicts, path, strlen(path), fc);
291 /* Many packages have installed the same file */
292 if (strcmp(sw_source->rankkey, fc->sw_source->rankkey) <= 0) {
293 /* Change sw source to the higher ranked one */
294 fc->sw_source = sw_source;
296 msmFreePointer((void**)&path);
302 rpmRC msm_plugin_tsm_pre(rpmPlugin plugin, rpmts ts)
305 rpmlog(RPMLOG_ERR, "Device security policy is missing. Unable to proceed\n");
312 rpmRC msm_plugin_psm_verify(rpmKeyring keyring, int sigTagId, pgpDigParams sig, DIGEST_CTX ctx, int rpmrc)
317 rpmlog(RPMLOG_ERR, "No device policy found\n");
321 if (rpmrc == RPMRC_NOKEY) {
322 /* No key, revert to unknown sw source. */
323 rpmlog(RPMLOG_ERR, "no key for signature, cannot search sw source\n");
327 /* RPM failed to verify signature */
328 rpmlog(RPMLOG_ERR, "Invalid signature, cannot search sw source\n");
331 if (sigTagId != RPMSIGTAG_RSA) {
332 /* Not RSA, revert to unknown sw source. */
333 rpmlog(RPMLOG_DEBUG, "not an RSA signature, cannot search sw source\n");
337 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceBySignature, sig, ctx);
339 rpmlog(RPMLOG_DEBUG, "signature matches sw source %s\n", current->name);
341 rpmlog(RPMLOG_DEBUG, "valid signature but no matching sw source\n");
345 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"_default_", NULL);
347 rpmlog(RPMLOG_DEBUG, "using _default_ sw source\n");
348 } else { // for now in case default sw source isn't there yet, allow to think that it is coming from root
349 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"root", NULL);
351 rpmlog(RPMLOG_DEBUG, "using _root_ sw source now for testing\n");
358 rpmRC msm_plugin_psm_pre(rpmPlugin plugin, rpmte te)
360 packagecontext *ctx = NULL;
361 manifest_x *mfx = NULL;
370 rpmlog(RPMLOG_ERR, "Device security policy is missing. Unable to proceed\n");
375 /* this means that verify hook has not been called */
376 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"_default_", NULL);
378 rpmlog(RPMLOG_DEBUG, "using _default_ sw source\n");
380 rpmlog(RPMLOG_ERR, "Default source isn't avaliable. Package source can't be determined. Abort installation\n");
387 rpmlog(RPMLOG_ERR, "Failed to create security context for %s\n", rpmteNEVRA(te));
391 if (rpmteType(ctx->te) == TR_REMOVED) {
392 /* Verify hook is not called before remove, */
393 /* so get the sw_source name from package header */
394 Header h = rpmteHeader(te);
396 const char *name = headerGetString(h, RPMTAG_SECSWSOURCE);
398 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)name, NULL);
399 rpmlog(RPMLOG_DEBUG, "removing %s from sw source %s\n",
400 rpmteN(ctx->te), name);
406 rpmlog(RPMLOG_INFO, "no sw source for removing %s\n", rpmteN(ctx->te));
412 rpmlog(RPMLOG_INFO, "No manifest in this package. Creating default one\n");
414 /* create default manifest manually. Make the package to belong to the domain where rpm is running */
416 mfx = calloc(1, sizeof(manifest_x));
418 mfx->sw_source = current;
419 mfx->name = strdup(rpmteN(ctx->te));
420 mfx->request = calloc(1, sizeof(request_x));
422 msmFreePointer((void**)&mfx->name);
423 msmFreePointer((void**)&mfx);
426 mfx->request->ac_domain = strdup(ownSmackLabel);
427 rpmlog(RPMLOG_DEBUG, "Done with manifest creation\n");
429 if (rpmBase64Decode(ctx->data, (void **) &xml, &xmllen) != 0) {
430 rpmlog(RPMLOG_ERR, "Failed to decode manifest for %s\n",
435 rpmlog(RPMLOG_DEBUG, "parsing %s manifest: \n%s", rpmteN(ctx->te), xml);
436 mfx = msmProcessManifestXml(xml, xmllen, current, rpmteN(ctx->te));
439 rpmlog(RPMLOG_ERR, "Failed to parse manifest for %s\n",
447 int res = smack_accesses_new(&(ctx->smack_accesses));
449 rpmlog(RPMLOG_ERR, "Failed to create smack access set\n");
453 if (rpmteType(ctx->te) == TR_ADDED) {
454 rpmlog(RPMLOG_DEBUG, "Installing the package\n");
455 package_x *package = NULL;
457 /*if (rootSWSource) {
458 // this is the first package that brings the policy
459 package = msmCreatePackage(mfx->name, mfx->sw_sources,
460 mfx->provides, NULL);
462 if (mfx->sw_source) {
463 /* all packages must have sw_source */
464 package = msmCreatePackage(mfx->name, mfx->sw_source,
465 mfx->provides, NULL);
467 rpmlog(RPMLOG_ERR, "Package doesn't have a sw source. Abnormal situation. Abort.\n");
472 rpmlog(RPMLOG_ERR, "Package could not be created. \n");
476 mfx->provides = NULL; /* owned by package now */
478 if (!package->sw_source) { /* this must never happen */
479 rpmlog(RPMLOG_ERR, "Install failed. Check that configuration has at least root sw source installed.\n");
480 msmFreePackage(package);
485 rpmlog(RPMLOG_DEBUG, "adding %s manifest data to system, package_name %s\n",
486 rpmteN(ctx->te), package->name);
488 if (msmSetupPackages(ctx->smack_accesses, package, package->sw_source)) {
489 rpmlog(RPMLOG_ERR, "Package setup failed for %s\n", rpmteN(ctx->te) );
490 msmFreePackage(package);
497 /*if (rootSWSource) {
498 // current configuration becomes dsp
502 rpmlog(RPMLOG_DEBUG, "Starting the security setup...\n");
503 unsigned int smackLabel = 0;
505 /*if (rootSWSource || ctx->mfx->sw_source) {*/
506 if (ctx->mfx->sw_source) {
507 if (ctx->mfx->sw_sources) {
508 smackLabel = 1; /* setting this one on since this manifest doesn't have any define/request section */
509 ret = msmSetupSWSources(ctx->smack_accesses, ctx->mfx, ts);
511 rpmlog(RPMLOG_ERR, "SW source setup failed for %s\n",
516 if (ctx->mfx->defines) {
517 ret = msmSetupDefines(ctx->smack_accesses, ctx->mfx);
519 rpmlog(RPMLOG_ERR, "AC domain setup failed for %s\n",
526 if (ctx->mfx->request) {
527 if (ctx->mfx->request->ac_domain)
529 ret = msmSetupRequests(ctx->mfx);
531 rpmlog(RPMLOG_ERR, "Request setup failed for %s\n",
536 if (package->provides) {
537 ret = msmSetupDBusPolicies(package, ctx->mfx);
539 rpmlog(RPMLOG_ERR, "Setting up dbus policies for %s failed\n",
544 if (ctx->smack_accesses) {
545 ret = msmSetupSmackRules(ctx->smack_accesses, ctx->mfx->name, 0, SmackEnabled);
546 smack_accesses_free(ctx->smack_accesses);
547 ctx->smack_accesses = NULL;
549 rpmlog(RPMLOG_ERR, "Setting up smack rules for %s failed\n",
555 /* last check is needed in order to catch in advance
556 the situation when no ac domain defined or requested */
557 if (smackLabel == 0) {
558 rpmlog(RPMLOG_ERR, "No ac domain defined or requested for package %s. Abort.\n", rpmteN(ctx->te));
563 } else if (rpmteDependsOn(ctx->te)) { /* TR_REMOVED */
565 rpmlog(RPMLOG_DEBUG, "upgrading package %s by %s\n",
566 rpmteNEVR(ctx->te), rpmteNEVR(rpmteDependsOn(ctx->te)));
567 } else if (mfx->sw_sources) {
568 rpmlog(RPMLOG_ERR, "Cannot remove sw source package %s\n",
573 rpmlog(RPMLOG_DEBUG, "Finished with pre psm hook \n");
577 fail: /* error, cancel the rpm operation */
580 exit: /* success, continue rpm operation */
582 msmFreePointer((void**)&xml);
587 rpmRC msm_plugin_fsm_file_init(const char* path, mode_t mode)
589 /* Check if there any conflicts that prevent file being written to the disk */
592 packagecontext *ctx = context;
593 char *cleanedPath = NULL, *dupPath = NULL;
595 rpmlog(RPMLOG_DEBUG, "Started with FSM_INIT_FUNC hook for file: %s\n", path);
597 if (!ctx) return RPMRC_FAIL;
598 if (!path) return RPMRC_FAIL;
600 dupPath = strdup(path);
601 cleanedPath = strchr(dupPath, ';');
605 HASH_FIND(hh, allfileconflicts, dupPath, strlen(dupPath), fc);
606 msmFreePointer((void**)&dupPath);
609 //rpmlog(RPMLOG_ERR, "rpmteN(ctx->te) %s fc->pkg_name: %s\n", rpmteN(ctx->te), fc->pkg_name);
610 /* There is a conflict, see if we are not allowed to overwrite */
612 (strcmp(current->rankkey, fc->sw_source->rankkey) > 0)) &&
613 (strcmp(rpmteN(ctx->te), fc->pkg_name))) {
614 rpmlog(RPMLOG_ERR, "%s has file security conflict in %s from sw source %s\n",
615 rpmteN(ctx->te), fc->path, fc->sw_source->name);
618 rpmlog(RPMLOG_DEBUG, "%s from %s overwrites %s from %s\n",
619 rpmteN(ctx->te), current->name, fc->path, fc->sw_source->name);
622 rpmlog(RPMLOG_DEBUG, "Finished with FSM_INIT_FUNC hook for file: %s\n", path);
627 rpmRC msm_plugin_fsm_file_commit(const char* path, mode_t mode, int type)
629 /* Setup xattrs for the given path */
631 packagecontext *ctx = context;
633 rpmlog(RPMLOG_DEBUG, "Started with FSM_COMMIT_FUNC hook for file: %s\n", path);
635 if (!ctx) return RPMRC_FAIL;
636 if (!path) return RPMRC_FAIL;
638 /* the type option is ignored for now */
641 file_x *file = xcalloc(1, sizeof(*file));
643 file->path = strndup(path, strlen(path) + 1);
644 LISTADD(ctx->mfx->files, file);
645 if (rpmteType(ctx->te) == TR_ADDED) {
646 if (msmSetFileXAttributes(ctx->mfx, file->path, cookie) < 0) {
647 rpmlog(RPMLOG_ERR, "Setting of extended attributes failed for file %s from package %s\n",
648 file->path, rpmteN(ctx->te));
656 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
661 rpmlog(RPMLOG_DEBUG, "Finished with FSM_COMMIT_FUNC hook for file: %s\n", path);
665 rpmRC msm_plugin_psm_post(rpmPlugin plugin, rpmte te, int rpmrc)
668 packagecontext *ctx = context;
669 if (!ctx) return RPMRC_FAIL;
671 if (!package_created) {
672 /* failure in rpm pre psm hook, rollback */
677 /* failure in rpm psm, rollback */
678 if (rpmteType(ctx->te) == TR_ADDED)
679 msmCancelPackage(ctx->mfx->name);
684 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
689 /* if (rootSWSource) {
690 // current configuration becomes dsp
694 if (rpmteType(ctx->te) == TR_REMOVED) {
695 if (ctx->mfx->sw_source) {
696 if (rpmteDependsOn(ctx->te)) {
697 rpmlog(RPMLOG_DEBUG, "upgrading %s manifest data\n",
700 rpmlog(RPMLOG_DEBUG, "removing %s manifest data\n",
702 if (ctx->mfx->defines || ctx->mfx->provides || ctx->mfx->sw_sources) {
703 msmRemoveRules(ctx->smack_accesses, ctx->mfx, SmackEnabled);
705 msmRemoveConfig(ctx->mfx);
713 rpmRC msm_plugin_tsm_post(rpmPlugin plugin, rpmts ts, int rpmrc)
715 packagecontext *ctx = context;
716 msmFreeInternalHashes(); // free hash structures first
719 msmSaveDeviceSecPolicyXml(dsp, ts->rootDir);
720 /* if (!rootSWSource) dsp = msmFreeManifestXml(dsp); */
721 dsp = msmFreeManifestXml(dsp);
724 if (!ctx) return RPMRC_FAIL;
728 rpmRC msm_plugin_cleanup(rpmPlugin plugin)
733 contextsHead = contextsTail = msmFree(contextsHead);
734 contextsHead = contextsTail = NULL;
736 if (allfileconflicts) {
737 fileconflict *fc, *temp;
738 HASH_ITER(hh, allfileconflicts, fc, temp) {
739 HASH_DELETE(hh, allfileconflicts, fc);
740 msmFreePointer((void**)&fc->path);
741 msmFreePointer((void**)&fc->pkg_name);
742 msmFreePointer((void**)&fc);
746 msmFreePointer((void**)&ownSmackLabel);
756 /*hooks that are used in msm plugin */
757 struct rpmPluginHooks_s msm_hooks = {
758 .init = msm_plugin_init,
759 .cleanup = msm_plugin_cleanup,
760 .tsm_pre = msm_plugin_tsm_pre,
761 .tsm_post = msm_plugin_tsm_post,
762 .psm_pre = msm_plugin_psm_pre,
763 .psm_post = msm_plugin_psm_post,
764 .psm_verify = msm_plugin_psm_verify,
765 .fsm_file_init = msm_plugin_fsm_file_init,
766 .fsm_file_commit = msm_plugin_fsm_file_commit,
767 .fsm_file_conflict = msm_plugin_fsm_file_conflict,