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"
49 /*hooks that are used in msm plugin */
51 rpmPluginHook PLUGIN_HOOKS = \
53 PLUGINHOOK_CLEANUP | \
54 PLUGINHOOK_TSM_PRE | \
55 PLUGINHOOK_TSM_POST | \
56 PLUGINHOOK_PSM_PRE | \
57 PLUGINHOOK_PSM_POST | \
58 PLUGINHOOK_FSM_COMMIT | \
59 PLUGINHOOK_FSM_INIT | \
60 PLUGINHOOK_FILE_CONFLICT | \
64 typedef struct fileconflict {
67 sw_source_x *sw_source;
71 typedef struct packagecontext {
72 char *data; /*!< base64 manifest data */
73 manifest_x *mfx; /*!< parsed manifest data */
74 rpmte te; /*!< related te */
75 struct packagecontext *next; /*!< next in linked list */
76 struct smack_accesses *smack_accesses; /*!< handle to smack_accesses */
79 static rpmts ts = NULL; /* rpm transaction */
80 static manifest_x *dsp = NULL; /* pointer to device security policy file */
81 static packagecontext *context = NULL;
82 static sw_source_x *current = NULL;
83 static packagecontext *contextsHead = NULL;
84 static packagecontext *contextsTail = NULL;
85 static fileconflict *allfileconflicts = NULL;
86 static char* ownSmackLabel = NULL; /* rpm smack security context */
87 static int SmackEnabled = 0; /* indicates if Smack is enabled in kernel or not */
88 static magic_t cookie = NULL;
89 static int package_created = 0;
91 /* Support functions */
94 * Frees the given pointer and sets it to NULL
95 * @param ptr address of pointer to be freed
98 static packagecontext *msmFree(packagecontext *ctx)
101 packagecontext *next = ctx->next;
102 msmFreePointer((void**)&ctx->data);
103 ctx->mfx = msmFreeManifestXml(ctx->mfx);
104 if (ctx->smack_accesses) smack_accesses_free(ctx->smack_accesses);
105 msmFreePointer((void**)&ctx);
112 * Frees the given pointer and sets it to NULL
113 * @param ptr address of pointer to be freed
116 void msmFreePointer(void** ptr)
125 * Creates a new package context
126 * @param te rpm te struct
127 * @return created package context
129 static packagecontext *msmNew(rpmte te)
134 packagecontext *ctx = NULL;
135 const char *sw_source = NULL;
142 ctx = xcalloc(1, sizeof(*ctx));
148 if (!headerIsEntry(h, RPMTAG_SECMANIFEST)) {
152 if (!headerGet(h, RPMTAG_SECMANIFEST, &msm, HEADERGET_MINMEM)) {
156 count = rpmtdCount(&msm);
161 ctx->data = xstrdup(rpmtdNextString(&msm));
162 rpmlog(RPMLOG_DEBUG, "%s manifest b64 data: %.40s...\n",
163 rpmteN(ctx->te), ctx->data);
167 if (rpmteType(ctx->te) == TR_ADDED) {
168 /* Save sw_source name into database, we need it when package */
169 /* is removed because signature verify is not called then. */
170 if (current) sw_source = current->name;
171 if (!sw_source || !headerPutString(h, RPMTAG_SECSWSOURCE, sw_source)) {
172 rpmlog(RPMLOG_ERR, "Failed to save sw source for %s, sw_source: %s\n",
173 rpmteN(ctx->te), sw_source);
174 msmFreePointer((void**)&ctx->data);
175 msmFreePointer((void**)&ctx);
184 * Creates and adds a te package context to a context list
185 * @param te rpm te struct
186 * @return created and added package context
188 static packagecontext *msmAddTE(rpmte te)
190 packagecontext *ctx = msmNew(te);
192 /* add the new policy to the list */
197 if (rpmteType(te) == TR_ADDED) {
198 /* add to the end of the list */
199 contextsTail->next = ctx;
202 /* add to the beginning of the list */
203 ctx->next = contextsHead;
211 /* -------------------- */
213 /* Implementation of hooks */
214 /* Hooks are listed in the call sequence inside rpm code */
216 rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *name, const char *opts)
223 if (msmLoadDeviceSecurityPolicy(ts->rootDir, &dsp) == RPMRC_FAIL)
229 /* smackfs path doesn't need to be prefixed with ts->rootDir
230 since it is only used for loading rules to kernel and libsmack will load it
231 by itself to the correct path */
233 if (smack_smackfs_path() == NULL) {
234 rpmlog(RPMLOG_INFO, "Smackfs isn't mounted at %s. Going to the image build mode. \n", smack_smackfs_path());
235 ownSmackLabel = strdup("_");
238 /* check its own security context and store it for the case when packages without manifest will be installed */
239 int res = smack_new_label_from_self(&ownSmackLabel);
242 rpmlog(RPMLOG_ERR, "Failed to obtain rpm security context\n");
247 rpmlog(RPMLOG_DEBUG, "rpm security context: %s\n", ownSmackLabel);
249 if (msmSetupSmackRulesDir(ts->rootDir) == RPMRC_FAIL)
252 cookie = magic_open(0);
256 if (magic_load(cookie, NULL) != 0) {
257 rpmlog(RPMLOG_ERR, "cannot load magic database - %s\n", magic_error(cookie));
266 rpmRC PLUGINHOOK_FILE_CONFLICT_FUNC(rpmts ts, char* path,
267 Header oldHeader, rpmfi oldFi,
271 if ((!path) || (!dsp))
274 rpmlog(RPMLOG_DEBUG, "FILE_CONFLICT_FUNC hook path %s\n",path);
276 /* uncomment this code if you wish that plugin obeys --replacefiles rpm flag
277 if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
281 const char *sw_source_name = headerGetString(oldHeader, RPMTAG_SECSWSOURCE);
282 const char *pkg_name = headerGetString(oldHeader, RPMTAG_NAME);
283 if (!sw_source_name || !pkg_name) {
284 return rpmrc; /* no sw source or package name - abnormal state */
287 sw_source_x *sw_source = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)sw_source_name, NULL);
289 return rpmrc; /* no old sw_source - abnormal state */
291 HASH_FIND(hh, allfileconflicts, path, strlen(path), fc);
293 /* Add new file conflict into hash */
294 fc = xcalloc(1, sizeof(*fc));
295 if (!fc) return RPMRC_FAIL;
297 fc->sw_source = sw_source;
298 fc->pkg_name = strdup(pkg_name);
299 if (!fc->pkg_name) return RPMRC_FAIL;
300 HASH_ADD_KEYPTR(hh, allfileconflicts, path, strlen(path), fc);
302 /* Many packages have installed the same file */
303 if (strcmp(sw_source->rankkey, fc->sw_source->rankkey) <= 0) {
304 /* Change sw source to the higher ranked one */
305 fc->sw_source = sw_source;
307 msmFreePointer((void**)&path);
313 rpmRC PLUGINHOOK_TSM_PRE_FUNC(rpmts ts)
316 rpmlog(RPMLOG_ERR, "Device security policy is missing. Unable to proceed\n");
323 rpmRC PLUGINHOOK_VERIFY_FUNC(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, DIGEST_CTX ctx, int rpmrc)
328 rpmlog(RPMLOG_ERR, "No device policy found\n");
332 if (rpmrc == RPMRC_NOKEY) {
333 /* No key, revert to unknown sw source. */
334 rpmlog(RPMLOG_ERR, "no key for signature, cannot search sw source\n");
338 /* RPM failed to verify signature */
339 rpmlog(RPMLOG_ERR, "Invalid signature, cannot search sw source\n");
342 if (sigtd->tag != RPMSIGTAG_RSA) {
343 /* Not RSA, revert to unknown sw source. */
344 rpmlog(RPMLOG_DEBUG, "not an RSA signature, cannot search sw source\n");
348 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceBySignature, sig, ctx);
350 rpmlog(RPMLOG_DEBUG, "signature matches sw source %s\n", current->name);
352 rpmlog(RPMLOG_DEBUG, "valid signature but no matching sw source\n");
356 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"_default_", NULL);
358 rpmlog(RPMLOG_DEBUG, "using _default_ sw source\n");
359 } else { // for now in case default sw source isn't there yet, allow to think that it is coming from root
360 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"root", NULL);
362 rpmlog(RPMLOG_DEBUG, "using _root_ sw source now for testing\n");
369 rpmRC PLUGINHOOK_PSM_PRE_FUNC(rpmte te)
371 packagecontext *ctx = NULL;
372 manifest_x *mfx = NULL;
381 rpmlog(RPMLOG_ERR, "Device security policy is missing. Unable to proceed\n");
386 /* this means that verify hook has not been called */
387 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)"_default_", NULL);
389 rpmlog(RPMLOG_DEBUG, "using _default_ sw source\n");
391 rpmlog(RPMLOG_ERR, "Default source isn't avaliable. Package source can't be determined. Abort installation\n");
398 rpmlog(RPMLOG_ERR, "Failed to create security context for %s\n", rpmteNEVRA(te));
402 if (rpmteType(ctx->te) == TR_REMOVED) {
403 /* Verify hook is not called before remove, */
404 /* so get the sw_source name from package header */
405 Header h = rpmteHeader(te);
407 const char *name = headerGetString(h, RPMTAG_SECSWSOURCE);
409 current = msmSWSourceTreeTraversal(dsp->sw_sources, msmFindSWSourceByName, (void *)name, NULL);
410 rpmlog(RPMLOG_DEBUG, "removing %s from sw source %s\n",
411 rpmteN(ctx->te), name);
417 rpmlog(RPMLOG_INFO, "no sw source for removing %s\n", rpmteN(ctx->te));
423 rpmlog(RPMLOG_INFO, "No manifest in this package. Creating default one\n");
425 /* create default manifest manually. Make the package to belong to the domain where rpm is running */
427 mfx = calloc(1, sizeof(manifest_x));
429 mfx->sw_source = current;
430 mfx->name = strdup(rpmteN(ctx->te));
431 mfx->request = calloc(1, sizeof(request_x));
433 msmFreePointer((void**)&mfx->name);
434 msmFreePointer((void**)&mfx);
437 mfx->request->ac_domain = strdup(ownSmackLabel);
438 rpmlog(RPMLOG_DEBUG, "Done with manifest creation\n");
440 if (rpmBase64Decode(ctx->data, (void **) &xml, &xmllen) != 0) {
441 rpmlog(RPMLOG_ERR, "Failed to decode manifest for %s\n",
446 rpmlog(RPMLOG_DEBUG, "parsing %s manifest: \n%s", rpmteN(ctx->te), xml);
447 mfx = msmProcessManifestXml(xml, xmllen, current, rpmteN(ctx->te));
450 rpmlog(RPMLOG_ERR, "Failed to parse manifest for %s\n",
458 int res = smack_accesses_new(&(ctx->smack_accesses));
460 rpmlog(RPMLOG_ERR, "Failed to create smack access set\n");
464 if (rpmteType(ctx->te) == TR_ADDED) {
465 rpmlog(RPMLOG_DEBUG, "Installing the package\n");
466 package_x *package = NULL;
468 /*if (rootSWSource) {
469 // this is the first package that brings the policy
470 package = msmCreatePackage(mfx->name, mfx->sw_sources,
471 mfx->provides, NULL);
473 if (mfx->sw_source) {
474 /* all packages must have sw_source */
475 package = msmCreatePackage(mfx->name, mfx->sw_source,
476 mfx->provides, NULL);
478 rpmlog(RPMLOG_ERR, "Package doesn't have a sw source. Abnormal situation. Abort.\n");
483 rpmlog(RPMLOG_ERR, "Package could not be created. \n");
487 mfx->provides = NULL; /* owned by package now */
489 if (!package->sw_source) { /* this must never happen */
490 rpmlog(RPMLOG_ERR, "Install failed. Check that configuration has at least root sw source installed.\n");
491 msmFreePackage(package);
496 rpmlog(RPMLOG_DEBUG, "adding %s manifest data to system, package_name %s\n",
497 rpmteN(ctx->te), package->name);
499 if (msmSetupPackages(ctx->smack_accesses, package, package->sw_source)) {
500 rpmlog(RPMLOG_ERR, "Package setup failed for %s\n", rpmteN(ctx->te) );
501 msmFreePackage(package);
508 /*if (rootSWSource) {
509 // current configuration becomes dsp
513 rpmlog(RPMLOG_DEBUG, "Starting the security setup...\n");
514 unsigned int smackLabel = 0;
516 /*if (rootSWSource || ctx->mfx->sw_source) {*/
517 if (ctx->mfx->sw_source) {
518 if (ctx->mfx->sw_sources) {
519 smackLabel = 1; /* setting this one on since this manifest doesn't have any define/request section */
520 ret = msmSetupSWSources(ctx->smack_accesses, ctx->mfx, ts);
522 rpmlog(RPMLOG_ERR, "SW source setup failed for %s\n",
527 if (ctx->mfx->defines) {
528 ret = msmSetupDefines(ctx->smack_accesses, ctx->mfx);
530 rpmlog(RPMLOG_ERR, "AC domain setup failed for %s\n",
537 if (ctx->mfx->request) {
538 if (ctx->mfx->request->ac_domain)
540 ret = msmSetupRequests(ctx->mfx);
542 rpmlog(RPMLOG_ERR, "Request setup failed for %s\n",
547 if (package->provides) {
548 ret = msmSetupDBusPolicies(package, ctx->mfx);
550 rpmlog(RPMLOG_ERR, "Setting up dbus policies for %s failed\n",
555 if (ctx->smack_accesses) {
556 ret = msmSetupSmackRules(ctx->smack_accesses, ctx->mfx->name, 0, SmackEnabled);
557 smack_accesses_free(ctx->smack_accesses);
558 ctx->smack_accesses = NULL;
560 rpmlog(RPMLOG_ERR, "Setting up smack rules for %s failed\n",
566 /* last check is needed in order to catch in advance
567 the situation when no ac domain defined or requested */
568 if (smackLabel == 0) {
569 rpmlog(RPMLOG_ERR, "No ac domain defined or requested for package %s. Abort.\n", rpmteN(ctx->te));
574 } else if (rpmteDependsOn(ctx->te)) { /* TR_REMOVED */
576 rpmlog(RPMLOG_DEBUG, "upgrading package %s by %s\n",
577 rpmteNEVR(ctx->te), rpmteNEVR(rpmteDependsOn(ctx->te)));
578 } else if (mfx->sw_sources) {
579 rpmlog(RPMLOG_ERR, "Cannot remove sw source package %s\n",
584 rpmlog(RPMLOG_DEBUG, "Finished with pre psm hook \n");
588 fail: /* error, cancel the rpm operation */
591 exit: /* success, continue rpm operation */
593 msmFreePointer((void**)&xml);
598 rpmRC PLUGINHOOK_FSM_INIT_FUNC(const char* path, mode_t mode)
600 /* Check if there any conflicts that prevent file being written to the disk */
603 packagecontext *ctx = context;
604 char *cleanedPath = NULL, *dupPath = NULL;
606 rpmlog(RPMLOG_DEBUG, "Started with FSM_INIT_FUNC hook for file: %s\n", path);
608 if (!ctx) return RPMRC_FAIL;
609 if (!path) return RPMRC_FAIL;
611 dupPath = strdup(path);
612 cleanedPath = strchr(dupPath, ';');
616 HASH_FIND(hh, allfileconflicts, dupPath, strlen(dupPath), fc);
617 msmFreePointer((void**)&dupPath);
620 //rpmlog(RPMLOG_DEBUG, "rpmteN(ctx->te) %s fc->pkg_name: %s\n", rpmteN(ctx->te), fc->pkg_name);
621 /* There is a conflict, see if we are not allowed to overwrite */
623 (strcmp(current->rankkey, fc->sw_source->rankkey) >= 0)) &&
624 (strcmp(rpmteN(ctx->te), fc->pkg_name))) {
625 rpmlog(RPMLOG_ERR, "%s has file conflict in %s from sw source %s\n",
626 rpmteN(ctx->te), fc->path, fc->sw_source->name);
629 rpmlog(RPMLOG_DEBUG, "%s from %s overwrites %s from %s\n",
630 rpmteN(ctx->te), current->name, fc->path, fc->sw_source->name);
633 rpmlog(RPMLOG_DEBUG, "Finished with FSM_INIT_FUNC hook for file: %s\n", path);
638 rpmRC PLUGINHOOK_FSM_COMMIT_FUNC(const char* path, mode_t mode, int type)
640 /* Setup xattrs for the given path */
642 packagecontext *ctx = context;
644 rpmlog(RPMLOG_DEBUG, "Started with FSM_COMMIT_FUNC hook for file: %s\n", path);
646 if (!ctx) return RPMRC_FAIL;
647 if (!path) return RPMRC_FAIL;
649 /* the type option is ignored for now */
652 file_x *file = xcalloc(1, sizeof(*file));
654 file->path = strndup(path, strlen(path) + 1);
655 LISTADD(ctx->mfx->files, file);
656 if (rpmteType(ctx->te) == TR_ADDED) {
657 if (msmSetFileXAttributes(ctx->mfx, file->path, cookie) < 0) {
658 rpmlog(RPMLOG_ERR, "Setting of extended attributes failed for file %s from package %s\n",
659 file->path, rpmteN(ctx->te));
667 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
672 rpmlog(RPMLOG_DEBUG, "Finished with FSM_COMMIT_FUNC hook for file: %s\n", path);
676 rpmRC PLUGINHOOK_PSM_POST_FUNC(rpmte te, int rpmrc)
679 packagecontext *ctx = context;
680 if (!ctx) return RPMRC_FAIL;
682 if (!package_created) {
683 /* failure in rpm pre psm hook, rollback */
688 /* failure in rpm psm, rollback */
689 if (rpmteType(ctx->te) == TR_ADDED)
690 msmCancelPackage(ctx->mfx->name);
695 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
700 /* if (rootSWSource) {
701 // current configuration becomes dsp
705 if (rpmteType(ctx->te) == TR_REMOVED) {
706 if (ctx->mfx->sw_source) {
707 if (rpmteDependsOn(ctx->te)) {
708 rpmlog(RPMLOG_DEBUG, "upgrading %s manifest data\n",
711 rpmlog(RPMLOG_DEBUG, "removing %s manifest data\n",
713 if (ctx->mfx->defines || ctx->mfx->provides || ctx->mfx->sw_sources) {
714 msmRemoveRules(ctx->smack_accesses, ctx->mfx, SmackEnabled);
716 msmRemoveConfig(ctx->mfx);
724 rpmRC PLUGINHOOK_TSM_POST_FUNC(rpmts ts, int rpmrc)
726 packagecontext *ctx = context;
727 msmFreeInternalHashes(); // free hash structures first
730 msmSaveDeviceSecPolicyXml(dsp, ts->rootDir);
731 /* if (!rootSWSource) dsp = msmFreeManifestXml(dsp); */
732 dsp = msmFreeManifestXml(dsp);
735 if (!ctx) return RPMRC_FAIL;
739 rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
744 contextsHead = contextsTail = msmFree(contextsHead);
745 contextsHead = contextsTail = NULL;
747 if (allfileconflicts) {
748 fileconflict *fc, *temp;
749 HASH_ITER(hh, allfileconflicts, fc, temp) {
750 HASH_DELETE(hh, allfileconflicts, fc);
751 msmFreePointer((void**)&fc->path);
752 msmFreePointer((void**)&fc->pkg_name);
753 msmFreePointer((void**)&fc);
757 msmFreePointer((void**)&ownSmackLabel);
758 if (cookie) magic_close(cookie);