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
33 #include <sys/types.h>
36 #include <sys/prctl.h>
37 #include <sys/capability.h>
40 #include <rpm/rpmfileutil.h>
41 #include <rpm/rpmmacro.h>
42 #include <rpm/rpmpgp.h>
43 #include <rpm/rpmkeyring.h>
44 #include <rpm/rpmdb.h>
46 #include "rpmio/rpmbase64.h"
47 #include "rpmio/rpmlog.h"
51 /*hooks that are used in msm plugin */
53 rpmPluginHook PLUGIN_HOOKS = \
55 PLUGINHOOK_CLEANUP | \
56 PLUGINHOOK_TSM_PRE | \
57 PLUGINHOOK_TSM_POST | \
58 PLUGINHOOK_PSM_PRE | \
59 PLUGINHOOK_PSM_POST | \
60 PLUGINHOOK_FSM_COMMIT | \
61 PLUGINHOOK_FSM_INIT | \
62 PLUGINHOOK_FILE_CONFLICT | \
66 typedef struct fileconflict {
69 sw_source_x *sw_source;
73 typedef struct packagecontext {
74 char *data; /*!< base64 manifest data */
75 manifest_x *mfx; /*!< parsed manifest data */
76 rpmte te; /*!< related te */
77 struct packagecontext *next; /*!< next in linked list */
78 struct smack_accesses *smack_accesses; /*!< handle to smack_accesses */
81 static rpmts ts = NULL;
82 static int rootSWSource= 0;
83 static manifest_x *root = NULL; /* pointer to device security policy file */
84 static packagecontext *context = NULL;
85 static sw_source_x *current = NULL;
86 static packagecontext *contextsHead = NULL;
87 static packagecontext *contextsTail = NULL;
88 static fileconflict *allfileconflicts = NULL;
89 static char* ownSmackLabel = NULL;
90 static int SmackEnabled = 0;
91 static magic_t cookie = NULL;
92 static int package_created = 0;
94 rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *name, const char *opts)
99 rpmlog(RPMLOG_INFO, "reading device security policy from %s\n", DEVICE_SECURITY_POLICY);
100 root = msmProcessDevSecPolicyXml(DEVICE_SECURITY_POLICY);
103 if (msmSetupSWSources(NULL, root, NULL)) {
104 rpmlog(RPMLOG_ERR, "Failed to setup device security policy from %s\n",
105 DEVICE_SECURITY_POLICY);
109 /* Do not allow plug-in to proceed without security policy existing */
110 rpmlog(RPMLOG_ERR, "Failed to process sw sources from %s\n",
111 DEVICE_SECURITY_POLICY);
115 /* check its own security context and store it for the case when packages without manifest will be installed */
118 if (stat(SMACK_LOAD_PATH, &buf) == 0) {
119 res = smack_new_label_from_self(&ownSmackLabel);
122 rpmlog(RPMLOG_ERR, "Failed to obtain rpm security context\n");
126 rpmlog(RPMLOG_INFO, "Smack disabled in kernel. Going to the image build mode. \n");
127 ownSmackLabel = strdup("_");
131 if (stat(SMACK_RULES_PATH, &buf) != 0) {
132 rpmlog(RPMLOG_INFO, "A directory for writing smack rules is missing. Creating one.\n");
133 mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH; // 644 -rwer--r--
134 if (stat(SMACK_RULES_PATH_BEG, &buf) != 0) {
135 if (mkdir(SMACK_RULES_PATH_BEG, mode) != 0) {
136 rpmlog(RPMLOG_ERR, "Failed to create a sub-directory for smack rules\n");
140 if (mkdir(SMACK_RULES_PATH, mode) != 0){
141 rpmlog(RPMLOG_ERR, "Failed to create a directory for smack rules\n");
146 rpmlog(RPMLOG_DEBUG, "rpm security context: %s\n", ownSmackLabel);
148 cookie = magic_open(0);
152 if (magic_load(cookie, NULL) != 0) {
153 rpmlog(RPMLOG_ERR, "cannot load magic database - %s\n", magic_error(cookie));
162 static int findSWSourceByName(sw_source_x *sw_source, void *param, void* param2)
164 const char *name = (const char *)param;
165 return strcmp(sw_source->name, name);
168 rpmRC PLUGINHOOK_FILE_CONFLICT_FUNC(rpmts ts, char* path,
169 Header oldHeader, rpmfi oldFi,
175 rpmlog(RPMLOG_DEBUG, "FILE_CONFLICT_FUNC hook path %s\n",path);
177 const char *name = headerGetString(oldHeader, RPMTAG_SECSWSOURCE);
178 if (!name || !root) {
179 return rpmrc; /* no sw source(s) - abnormal state */
181 const char *pkg_name = headerGetString(oldHeader, RPMTAG_NAME);
183 sw_source_x *sw_source = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)name, NULL);
185 return rpmrc; /* no old sw_source - abnormal state */
187 HASH_FIND(hh, allfileconflicts, path, strlen(path), fc);
189 /* Add new file conflict into hash */
190 fc = xcalloc(1, sizeof(*fc));
191 if (!fc) return RPMRC_FAIL;
193 fc->sw_source = sw_source;
194 fc->pkg_name = pkg_name;
195 HASH_ADD_KEYPTR(hh, allfileconflicts, path, strlen(path), fc);
197 /* Many packages have installed the same file */
198 if (strcmp(sw_source->rankkey, fc->sw_source->rankkey) <= 0) {
199 /* Change sw source to the higher ranked one */
200 fc->sw_source = sw_source;
202 msmFreePointer((void**)&path);
205 if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
206 /* Conflict has been noted, now return ok. It will be actually */
207 /* resolved later when conflicting package signature is verified */
208 /* and sw_source is known. */
214 rpmRC PLUGINHOOK_TSM_PRE_FUNC(rpmts ts)
219 static int findSWSourceBySignature(sw_source_x *sw_source, void *param, void* param2)
223 pgpDigParams sig = (pgpDigParams)param;
224 DIGEST_CTX ctx = (DIGEST_CTX)param2;
225 pgpDigParams key = NULL;
228 for (origin = sw_source->origins; origin; origin = origin->prev) {
229 for (keyinfo = origin->keyinfos; keyinfo; keyinfo = keyinfo->prev) {
230 if (pgpPrtParams(keyinfo->keydata, keyinfo->keylen, PGPTAG_PUBLIC_KEY, &key)) {
231 rpmlog(RPMLOG_INFO, "invalid sw source key\n");
234 if (pgpVerifySignature(key, sig, ctx) == RPMRC_OK) {
242 rpmRC PLUGINHOOK_VERIFY_FUNC(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, DIGEST_CTX ctx, int rpmrc)
248 if (rpmrc == RPMRC_NOKEY) {
249 rpmlog(RPMLOG_INFO, "package verified as root sw source\n");
250 rootSWSource = 1; /* accept any signed package as root */
253 rpmlog(RPMLOG_ERR, "No device security policy, cannot verify signature\n");
258 // make currently that even non-signed package with root policy will be treated as trusted
261 rpmlog(RPMLOG_INFO, "package verified as root sw source\n");
262 rootSWSource = 1; /* accept any signed package as root */
270 rpmlog(RPMLOG_INFO, "No device policy found\n");
271 rootSWSource = 1; /* accept any signed package as root */
275 if (rpmrc == RPMRC_NOKEY) {
276 /* No key, revert to unknown sw source. */
277 rpmlog(RPMLOG_INFO, "no key for signature, cannot search sw source\n");
281 /* RPM failed to verify signature */
282 rpmlog(RPMLOG_ERR, "Invalid signature, cannot search sw source\n");
285 if (sigtd->tag != RPMSIGTAG_RSA) {
286 /* Not RSA, revert to unknown sw source. */
287 rpmlog(RPMLOG_INFO, "no RSA signature, cannot search sw source\n");
290 current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceBySignature, sig, ctx);
292 rpmlog(RPMLOG_INFO, "signature matches sw source %s\n", current->name);
294 rpmlog(RPMLOG_INFO, "valid signature but no matching sw source\n");
298 current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"_default_", NULL);
300 rpmlog(RPMLOG_INFO, "using _default_ sw source\n");
301 else { // for now in case default sw source isn't there yet, allow to think that it is coming from root
302 current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"root", NULL);
304 rpmlog(RPMLOG_INFO, "using _root_ sw source now for testing\n");
311 static packagecontext *msmNew(rpmte te)
316 packagecontext *ctx = NULL;
317 const char *sw_source = NULL;
324 ctx = xcalloc(1, sizeof(*ctx));
330 if (!headerIsEntry(h, RPMTAG_SECMANIFEST)) {
334 if (!headerGet(h, RPMTAG_SECMANIFEST, &msm, HEADERGET_MINMEM)) {
338 count = rpmtdCount(&msm);
343 ctx->data = xstrdup(rpmtdNextString(&msm));
344 rpmlog(RPMLOG_INFO, "%s manifest b64 data: %.40s...\n",
345 rpmteN(ctx->te), ctx->data);
350 if (rpmteType(ctx->te) == TR_ADDED) {
351 /* Save sw_source name into database, we need it when package */
352 /* is removed because signature verify is not called then. */
353 if (current) sw_source = current->name;
354 else if (rootSWSource) sw_source = rpmteN(ctx->te);
356 if (!sw_source || !headerPutString(h, RPMTAG_SECSWSOURCE, sw_source)) {
357 rpmlog(RPMLOG_ERR, "Failed to save sw source for %s, sw_source: %s\n",
358 rpmteN(ctx->te), sw_source);
359 msmFreePointer((void**)&ctx->data);
360 msmFreePointer((void**)&ctx);
368 static packagecontext *msmAddTE(rpmte te)
370 packagecontext *ctx = msmNew(te);
372 /* add the new policy to the list */
377 if (rpmteType(te) == TR_ADDED) {
378 /* add to the end of the list */
379 contextsTail->next = ctx;
382 /* add to the beginning of the list */
383 ctx->next = contextsHead;
391 rpmRC PLUGINHOOK_PSM_PRE_FUNC(rpmte te)
393 packagecontext *ctx = NULL;
394 manifest_x *mfx = NULL;
400 if (!root && !rootSWSource) {
401 /* no sw source config, just exit */
406 /* this means that verify hook has not been called */
407 current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"_default_", NULL);
409 rpmlog(RPMLOG_INFO, "using _default_ sw source\n");
411 rpmlog(RPMLOG_ERR, "Default source isn't availiable. Package source can't be determined. Abort installation\n");
418 rpmlog(RPMLOG_ERR, "Failed to create security context for %s\n",
423 if (rpmteType(ctx->te) == TR_REMOVED) {
425 /* Verify hook is not called before remove, */
426 /* so get the sw_source name from package header */
427 Header h = rpmteHeader(te);
429 const char *name = headerGetString(h, RPMTAG_SECSWSOURCE);
431 current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)name, NULL);
432 rpmlog(RPMLOG_INFO, "removing %s from sw source %s\n",
433 rpmteN(ctx->te), name);
438 rpmlog(RPMLOG_INFO, "no sw source for removing %s\n", rpmteN(ctx->te));
444 rpmlog(RPMLOG_INFO, "No manifest in this package. Creating default one\n");
446 /* create default manifest manually. Make the package to belong to the domain where rpm is running */
448 mfx = calloc(1, sizeof(manifest_x));
450 mfx->sw_source = current;
451 mfx->name = strdup(rpmteN(ctx->te));
452 mfx->request = calloc(1, sizeof(request_x));
454 msmFreePointer((void**)&mfx->name);
455 msmFreePointer((void**)&mfx);
458 mfx->request->ac_domain = strdup(ownSmackLabel);
459 rpmlog(RPMLOG_DEBUG, "Done with manifest creation\n");
462 if (rpmBase64Decode(ctx->data, (void **) &xml, &xmllen) != 0) {
463 rpmlog(RPMLOG_ERR, "Failed to decode manifest for %s\n",
468 rpmlog(RPMLOG_INFO, "parsing %s manifest: \n%s", rpmteN(ctx->te), xml);
469 mfx = msmProcessManifestXml(xml, xmllen, current, rpmteN(ctx->te));
472 rpmlog(RPMLOG_ERR, "Failed to parse manifest for %s\n",
481 int res = smack_accesses_new(&(ctx->smack_accesses));
483 rpmlog(RPMLOG_ERR, "Failed to create smack access set\n");
487 if (rpmteType(ctx->te) == TR_ADDED) {
489 rpmlog(RPMLOG_DEBUG, "Installing the package\n");
491 package_x *package = NULL;
494 /* this is the first package */
495 package = msmCreatePackage(mfx->name, mfx->sw_sources,
496 mfx->provides, NULL);
497 } else if (mfx->sw_source) {
498 /* all packages must have sw_source */
499 package = msmCreatePackage(mfx->name, mfx->sw_source,
500 mfx->provides, NULL);
502 rpmlog(RPMLOG_ERR, "Package doesn't have a sw source. Abnormal situation. Abort.\n");
507 rpmlog(RPMLOG_ERR, "Package could not be created. \n");
511 mfx->provides = NULL; /* owned by package now */
513 if (!package->sw_source) { /* this must never happen */
514 rpmlog(RPMLOG_ERR, "Install failed. Check that configuration has at least root sw source installed.\n");
515 msmFreePackage(package);
520 rpmlog(RPMLOG_INFO, "adding %s manifest data to system, package_name %s\n",
521 rpmteN(ctx->te), package->name);
523 if (msmSetupPackages(ctx->smack_accesses, package, package->sw_source)) {
524 rpmlog(RPMLOG_ERR, "Package setup failed for %s\n", rpmteN(ctx->te) );
525 msmFreePackage(package);
531 /* current is root */
535 rpmlog(RPMLOG_DEBUG, "Starting the security setup...\n");
536 unsigned int smackLabel = 0;
538 if (rootSWSource || ctx->mfx->sw_source) {
539 if (ctx->mfx->sw_sources) {
540 smackLabel = 1; /* setting this one on since this manifest doesn't have any define/request section */
541 ret = msmSetupSWSources(ctx->smack_accesses, ctx->mfx, ts);
543 rpmlog(RPMLOG_ERR, "SW source setup failed for %s\n",
545 msmCancelPackage(ctx->mfx->name);
549 if (ctx->mfx->define) {
550 if (ctx->mfx->define->name)
552 ret = msmSetupDefine(ctx->smack_accesses, ctx->mfx);
554 rpmlog(RPMLOG_ERR, "AC domain setup failed for %s\n",
556 msmCancelPackage(ctx->mfx->name);
560 if (ctx->mfx->request) {
561 if (ctx->mfx->request->ac_domain)
563 ret = msmSetupRequests(ctx->mfx);
565 rpmlog(RPMLOG_ERR, "Request setup failed for %s\n",
567 msmCancelPackage(ctx->mfx->name);
571 if (ctx->smack_accesses) {
572 ret = msmSetupSmackRules(ctx->smack_accesses, ctx->mfx->name, 0, SmackEnabled);
573 smack_accesses_free(ctx->smack_accesses);
574 ctx->smack_accesses = NULL;
576 rpmlog(RPMLOG_ERR, "Setting up smack rules for %s failed\n",
578 msmCancelPackage(ctx->mfx->name);
582 if (package->provides) {
583 ret = msmSetupDBusPolicies(package);
585 rpmlog(RPMLOG_ERR, "Setting up dbus policies for %s failed\n",
587 msmCancelPackage(ctx->mfx->name);
592 /* last check is needed in order to catch in advance
593 the situation when no ac domain defined or requested */
594 if (smackLabel == 0) {
595 rpmlog(RPMLOG_ERR, "No ac domain defined or requested for package %s. Abort.\n", rpmteN(ctx->te));
596 msmCancelPackage(ctx->mfx->name);
602 } else if (rpmteDependsOn(ctx->te)) { /* TR_REMOVED */
603 rpmlog(RPMLOG_INFO, "upgrading package %s by %s\n",
604 rpmteNEVR(ctx->te), rpmteNEVR(rpmteDependsOn(ctx->te)));
605 } else if (mfx->sw_sources) {
606 rpmlog(RPMLOG_ERR, "Cannot remove sw source package %s\n",
611 rpmlog(RPMLOG_DEBUG, "Finished with pre psm hook \n");
616 fail: /* error, cancel the rpm operation */
619 exit: /* success, continue rpm operation */
621 msmFreePointer((void**)&xml);
626 rpmRC PLUGINHOOK_FSM_INIT_FUNC(const char* path, mode_t mode)
629 //check if there any conflicts that prevent file being written to the disk
632 packagecontext *ctx = context;
633 char * cleanedPath = NULL, *dupPath = NULL;
635 rpmlog(RPMLOG_DEBUG, "Started with FSM_INIT_FUNC hook for file: %s\n", path);
637 if (!ctx) return RPMRC_FAIL;
638 if (!path) return RPMRC_FAIL;
640 dupPath = strdup(path);
641 cleanedPath = strchr(dupPath, ';');
645 //rpmlog(RPMLOG_DEBUG, "dupapth: %s\n", dupPath);
647 HASH_FIND(hh, allfileconflicts, dupPath, strlen(dupPath), fc);
648 msmFreePointer((void**)&dupPath);
651 //rpmlog(RPMLOG_DEBUG, "rpmteN(ctx->te) %s fc->pkg_name: %s\n", rpmteN(ctx->te), fc->pkg_name);
652 /* There is a conflict, see if we are not allowed to overwrite */
653 if ((!current || (strcmp(current->rankkey, fc->sw_source->rankkey) >= 0)) && (strcmp(rpmteN(ctx->te), fc->pkg_name))){
654 rpmlog(RPMLOG_ERR, "%s has file conflict in %s from sw source %s\n",
655 rpmteN(ctx->te), fc->path, fc->sw_source->name);
658 rpmlog(RPMLOG_INFO, "%s from %s overwrites %s from %s\n",
659 rpmteN(ctx->te), current->name, fc->path, fc->sw_source->name);
662 rpmlog(RPMLOG_DEBUG, "Finished with FSM_INIT_FUNC hook for file: %s\n", path);
667 rpmRC PLUGINHOOK_FSM_COMMIT_FUNC(const char* path, mode_t mode, int type)
669 packagecontext *ctx = context;
670 if (!ctx) return RPMRC_FAIL;
671 if (!path) return RPMRC_FAIL;
673 /* the type is ignored for now */
675 rpmlog(RPMLOG_DEBUG, "Started with FSM_COMMIT_FUNC hook for file: %s\n", path);
678 file_x *file = xcalloc(1, sizeof(*file));
680 file->path = strndup(path, strlen(path) + 1);
681 LISTADD(ctx->mfx->files, file);
682 if (rpmteType(ctx->te) == TR_ADDED) {
683 if (msmSetFileXAttributes(ctx->mfx, file->path, cookie) < 0) {
684 rpmlog(RPMLOG_ERR, "Setting of extended attributes failed for file %s from package %s\n",
685 file->path, rpmteN(ctx->te));
693 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
698 rpmlog(RPMLOG_DEBUG, "Finished with FSM_COMMIT_FUNC hook for file: %s\n", path);
702 rpmRC PLUGINHOOK_PSM_POST_FUNC(rpmte te, int rpmrc)
706 packagecontext *ctx = context;
707 if (!ctx) return RPMRC_FAIL;
709 if (!package_created) {
710 /* failure in rpm pre psm hook, rollback */
715 /* failure in rpm psm, rollback */
716 if (rpmteType(ctx->te) == TR_ADDED)
717 msmCancelPackage(ctx->mfx->name);
722 rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
728 /* current is root */
733 if (rpmteType(ctx->te) == TR_REMOVED) {
734 if (ctx->mfx->sw_source) {
735 if (rpmteDependsOn(ctx->te)) {
736 rpmlog(RPMLOG_INFO, "upgrading %s manifest data\n",
739 rpmlog(RPMLOG_INFO, "removing %s manifest data\n",
741 if (ctx->mfx->define || ctx->mfx->provides || ctx->mfx->sw_sources) {
742 msmRemoveRules(ctx->smack_accesses, ctx->mfx, SmackEnabled);
744 msmRemoveConfig(ctx->mfx);
759 rpmRC PLUGINHOOK_TSM_POST_FUNC(rpmts ts, int rpmrc)
761 packagecontext *ctx = context;
762 if (!ctx) return RPMRC_FAIL;
766 static packagecontext *msmFree(packagecontext *ctx)
770 packagecontext *next = ctx->next;
771 msmFreePointer((void**)&ctx->data);
772 ctx->mfx = msmFreeManifestXml(ctx->mfx);
773 if (ctx->smack_accesses) smack_accesses_free(ctx->smack_accesses);
774 msmFreePointer((void**)&ctx);
782 rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
785 msmFreeInternalHashes(); // free hash structures first
788 msmSaveDeviceSecPolicyXml(root);
789 if (!rootSWSource) root = msmFreeManifestXml(root);
794 contextsHead = contextsTail = msmFree(contextsHead);
795 contextsHead = contextsTail = NULL;
797 if (allfileconflicts) {
798 fileconflict *fc, *temp;
799 HASH_ITER(hh, allfileconflicts, fc, temp) {
800 HASH_DELETE(hh, allfileconflicts, fc);
801 msmFreePointer((void**)&fc->path);
802 msmFreePointer((void**)&fc);
806 msmFreePointer((void**)&ownSmackLabel);
807 if (cookie) magic_close(cookie);
812 const char *msmQueryPackageFile(const char *rfor,
813 const char **dname, const char **pname)
816 const char *path = NULL;
819 char *sep = strchr(rfor, ':');
820 if (sep && sep[1] == ':' && sep[2] == '/')
822 if (!path) return NULL;
824 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, path, 0);
826 mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, path, 0);
829 const char *name, *sw_source;
830 while ((h = rpmdbNextIterator(mi))) {
832 name = headerGetString(h, RPMTAG_NAME);
833 sw_source = headerGetString(h, RPMTAG_SECSWSOURCE);
834 if (name && sw_source) {
835 match = !strncmp(rfor, name, path - rfor - 2);
836 rpmlog(RPMLOG_INFO, "file %s belongs to package %s in sw source %s %s\n", path, name, sw_source, (match ? "(matched request)" : ""));
838 *pname = xstrdup(name);
839 *dname = xstrdup(sw_source);
844 mi = rpmdbFreeIterator(mi);
847 return match ? path : NULL;
850 void msmFreePointer(void** ptr)