Fix building issue for tizen_base(configure: error: --with-msm given, but attr/xattr...
[platform/upstream/rpm.git] / plugins / sepolicy.c
1 #include "plugin.h"
2
3 #include <errno.h>
4 #include <selinux/selinux.h>
5 #include <semanage/semanage.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8
9 #include <rpm/rpmpol.h>
10 #include <rpm/rpmfileutil.h>
11 #include <rpm/rpmmacro.h>
12 #include <rpm/rpmbase64.h>
13
14 #include "lib/rpmte_internal.h"
15 #include "lib/rpmts_internal.h" /* rpmtsSELabelFoo() */
16
17 rpmPluginHook PLUGIN_HOOKS = \
18         PLUGINHOOK_INIT | \
19         PLUGINHOOK_CLEANUP | \
20         PLUGINHOOK_OPENTE | \
21         PLUGINHOOK_COLL_POST_ADD | \
22         PLUGINHOOK_COLL_PRE_REMOVE;
23
24 typedef enum sepolAction {
25     SEPOL_ACTION_IGNORE,
26     SEPOL_ACTION_INSTALL,
27     SEPOL_ACTION_REMOVE
28 } sepolAction;
29
30 typedef struct sepol {
31     char *data;                 /*!< policy data */
32     char *name;                 /*!< policy names */
33     ARGV_t types;               /*!< policy types */
34     uint32_t flags;             /*!< policy flags */
35     sepolAction action;         /*!< install/remove/ignore */
36     struct sepol *next;         /*!< next in linked list */
37 } sepol;
38
39 typedef struct sepoltrans {
40     int execsemodule;           /*!< 0 = use libsemanage to install policy; non-zero = use semodule */
41     semanage_handle_t *sh;      /*!< handle to libsemanage, only used when execsemodule is zero */
42     char *semodulepath;         /*!< path to semodule binary */
43     ARGV_t semodargs;           /*!< argument list to pass to semodule, only used when execsemodule is non-zero */
44     ARGV_t filelist;            /*!< list of temporary files that have been written to disk during the transaction */
45     int changes;                /*!< number of changes made during the transaction */
46 } sepoltrans;
47
48
49 static char * name;
50 static rpmts ts;
51
52 static sepol * policiesHead;
53 static sepol * policiesTail;
54
55
56 static sepol *sepolNew(rpmte te);
57 static sepol *sepolFree(sepol * pol);
58 static int sepolHasType(const sepol * pol, const char *type);
59
60 static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype);
61 static rpmRC sepolWritePolicy(const sepol * pol, char **path);
62 static rpmRC sepolLoadPolicies(const sepol * pols);
63
64 static sepoltrans *sepoltransNew(void);
65 static sepoltrans *sepoltransFree(sepoltrans * pt);
66
67 static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol);
68 static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol);
69 static rpmRC sepoltransCommit(sepoltrans * pt);
70
71
72 static sepol *sepolNew(rpmte te)
73 {
74     sepol *head = NULL;
75     sepol *ret = NULL;
76     sepolAction action;
77     Header h;
78     struct rpmtd_s policies, names, types, typesidx, flags;
79     int i, j;
80     int count;
81
82     rpmtdReset(&policies);
83     rpmtdReset(&names);
84     rpmtdReset(&types);
85     rpmtdReset(&typesidx);
86     rpmtdReset(&flags);
87
88     h = rpmteHeader(te);
89     if (!h) {
90         goto exit;
91     }
92
93     if (!headerIsEntry(h, RPMTAG_POLICIES)) {
94         goto exit;
95     }
96
97     if (!headerGet(h, RPMTAG_POLICIES, &policies, HEADERGET_MINMEM)) {
98         goto exit;
99     }
100
101     count = rpmtdCount(&policies);
102     if (count <= 0) {
103         goto exit;
104     }
105
106     if (!headerGet(h, RPMTAG_POLICYNAMES, &names, HEADERGET_MINMEM)
107         || rpmtdCount(&names) != count) {
108         goto exit;
109     }
110
111     if (!headerGet(h, RPMTAG_POLICYFLAGS, &flags, HEADERGET_MINMEM)
112         || rpmtdCount(&flags) != count) {
113         goto exit;
114     }
115
116     if (!headerGet(h, RPMTAG_POLICYTYPES, &types, HEADERGET_MINMEM)) {
117         goto exit;
118     }
119
120     if (!headerGet(h, RPMTAG_POLICYTYPESINDEXES, &typesidx, HEADERGET_MINMEM)
121         || rpmtdCount(&types) != rpmtdCount(&typesidx)) {
122         goto exit;
123     }
124
125     action = (rpmteType(te) == TR_ADDED) ? SEPOL_ACTION_INSTALL : SEPOL_ACTION_REMOVE;
126
127     for (i = 0; i < count; i++) {
128         sepol *pol = xcalloc(1, sizeof(*pol));
129         pol->next = head;
130         head = pol;
131
132         pol->data = xstrdup(rpmtdNextString(&policies));
133         pol->name = xstrdup(rpmtdNextString(&names));
134         pol->flags = *rpmtdNextUint32(&flags);
135         pol->action = action;
136
137         for (j = 0; j < rpmtdCount(&types); j++) {
138             uint32_t index = ((uint32_t *) typesidx.data)[j];
139             if (index < 0 || index >= count) {
140                 goto exit;
141             }
142             if (index != i) {
143                 continue;
144             }
145             argvAdd(&pol->types, rpmtdNextString(&types));
146         }
147         argvSort(pol->types, NULL);
148     }
149
150     ret = head;
151
152   exit:
153     headerFree(h);
154
155     rpmtdFreeData(&policies);
156     rpmtdFreeData(&names);
157     rpmtdFreeData(&types);
158     rpmtdFreeData(&typesidx);
159     rpmtdFreeData(&flags);
160
161     if (!ret) {
162         sepolFree(head);
163     }
164
165     return ret;
166 }
167
168 static sepol *sepolFree(sepol * pol)
169 {
170     while (pol) {
171         sepol *next = pol->next;
172
173         pol->data = _free(pol->data);
174         pol->name = _free(pol->name);
175         pol->types = argvFree(pol->types);
176         pol->next = NULL;
177         _free(pol);
178
179         pol = next;
180     }
181
182     return NULL;
183 }
184
185 int sepolHasType(const sepol * pol, const char *type)
186 {
187     if (!pol || !type) {
188         return 0;
189     }
190
191     return (argvSearch(pol->types, type, NULL) != NULL) ||
192            (argvSearch(pol->types, RPMPOL_TYPE_DEFAULT, NULL) != NULL);
193 }
194
195 static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype)
196 {
197     sepol *pol;
198     rpmRC rc = RPMRC_OK;
199
200     for (pol = pols; pol; pol = pol->next) {
201         if (!sepolHasType(pol, policytype)) {
202             pol->action = SEPOL_ACTION_IGNORE;
203         }
204     }
205
206     return rc;
207 }
208
209 static rpmRC sepolWritePolicy(const sepol * pol, char **path)
210 {
211     char *tmppath = NULL;
212     FD_t fd = NULL;
213     char *policy = NULL;
214     size_t policylen;
215     rpmRC rc = RPMRC_FAIL;
216
217     if (rpmBase64Decode(pol->data, (void **) &policy, &policylen) != 0) {
218         rpmlog(RPMLOG_ERR, _("Failed to decode policy for %s\n"),
219                pol->name);
220         goto exit;
221     }
222
223     fd = rpmMkTempFile(NULL, &tmppath);
224     if (fd == NULL || Ferror(fd)) {
225         rpmlog(RPMLOG_ERR, _("Failed to create temporary file for %s: %s\n"),
226                pol->name, strerror(errno));
227         goto exit;
228     }
229
230     if (!Fwrite(policy, sizeof(*policy), policylen, fd)) {
231         rpmlog(RPMLOG_ERR, _("Failed to write %s policy to file %s\n"),
232                pol->name, tmppath);
233         goto exit;
234     }
235
236     *path = tmppath;
237     rc = RPMRC_OK;
238
239   exit:
240     if (fd)
241         Fclose(fd);
242     _free(policy);
243     if (rc != RPMRC_OK)
244         _free(tmppath);
245
246     return rc;
247 }
248
249 static rpmRC sepolLoadPolicies(const sepol * pols)
250 {
251     const sepol *pol;
252     rpmRC rc = RPMRC_FAIL;
253     sepoltrans *pt = sepoltransNew();
254
255     if (pt == NULL)
256         goto exit;
257
258     for (pol = pols; pol; pol = pol->next) {
259         switch (pol->action) {
260         case SEPOL_ACTION_REMOVE:
261             rc = sepoltransRemove(pt, pol);
262             break;
263         case SEPOL_ACTION_INSTALL:
264             rc = sepoltransInstall(pt, pol);
265             break;
266         case SEPOL_ACTION_IGNORE:
267         default:
268             rc = RPMRC_OK;
269             break;
270         }
271
272         if (rc != RPMRC_OK)
273             goto exit;
274     }
275
276     rc = sepoltransCommit(pt);
277
278 exit:
279     sepoltransFree(pt);
280
281     return rc;
282 }
283
284 static sepoltrans *sepoltransNew(void)
285 {
286     sepoltrans *pt = xcalloc(1, sizeof(*pt));
287     pt->semodulepath = rpmExpand("%{__semodule}", NULL);
288     pt->execsemodule = (!rpmChrootDone() && access(pt->semodulepath, X_OK) == 0);
289     pt->changes = 0;
290
291     if (pt->execsemodule) {
292         argvAdd(&pt->semodargs, "semodule");
293     } else {
294         pt->sh = semanage_handle_create();
295         if (!pt->sh) {
296             rpmlog(RPMLOG_ERR, _("Failed to create semanage handle\n"));
297             goto err;
298         }
299         semanage_set_create_store(pt->sh, 1);
300         semanage_set_check_contexts(pt->sh, 0);
301         if (semanage_connect(pt->sh) < 0) {
302             rpmlog(RPMLOG_ERR, _("Failed to connect to policy handler\n"));
303             goto err;
304         }
305         if (semanage_begin_transaction(pt->sh) < 0) {
306             rpmlog(RPMLOG_ERR, _("Failed to begin policy transaction: %s\n"),
307                    errno ? strerror(errno) : "");
308             goto err;
309         }
310         semanage_set_reload(pt->sh, !rpmChrootDone());
311     }
312
313     return pt;
314
315   err:
316     if (pt->sh) {
317         if (semanage_is_connected(pt->sh)) {
318             semanage_disconnect(pt->sh);
319         }
320         semanage_handle_destroy(pt->sh);
321     }
322     free(pt);
323
324     return NULL;
325 }
326
327 static sepoltrans *sepoltransFree(sepoltrans * pt)
328 {
329     ARGV_t file;
330
331     if (!pt) {
332         return NULL;
333     }
334
335     for (file = pt->filelist; file && *file; file++) {
336         if (unlink(*file) < 0) {
337             rpmlog(RPMLOG_WARNING, _("Failed to remove temporary policy file %s: %s\n"),
338                    *file, strerror(errno));
339         }
340     }
341     argvFree(pt->filelist);
342
343     if (pt->execsemodule) {
344         argvFree(pt->semodargs);
345     } else {
346         semanage_disconnect(pt->sh);
347         semanage_handle_destroy(pt->sh);
348     }
349
350     free(pt->semodulepath);
351     memset(pt, 0, sizeof(*pt)); /* trash and burn */
352
353     free(pt);
354     return NULL;
355 }
356
357 static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol)
358 {
359     rpmRC rc = RPMRC_OK;
360     char *path = NULL;
361
362     rc = sepolWritePolicy(pol, &path);
363     if (rc != RPMRC_OK) {
364         return rc;
365     }
366     argvAdd(&pt->filelist, path);
367
368     if (pt->execsemodule) {
369         const char *flag = (pol->flags & RPMPOL_FLAG_BASE) ? "-b" : "-i";
370         if (argvAdd(&pt->semodargs, flag) < 0 || argvAdd(&pt->semodargs, path) < 0) {
371             rc = RPMRC_FAIL;
372         }
373     } else {
374         if (pol->flags & RPMPOL_FLAG_BASE) {
375             if (semanage_module_install_base_file(pt->sh, path) < 0) {
376                 rc = RPMRC_FAIL;
377             }
378         } else {
379             if (semanage_module_install_file(pt->sh, path) < 0) {
380                 rc = RPMRC_FAIL;
381             }
382         }
383     }
384
385     if (rc != RPMRC_OK) {
386         rpmlog(RPMLOG_ERR, _("Failed to install policy module: %s (%s)\n"),
387                pol->name, path);
388     } else {
389         pt->changes++;
390     }
391
392     _free(path);
393
394     return rc;
395 }
396
397 static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol)
398 {
399     rpmRC rc = RPMRC_OK;
400
401     if (pol->flags & RPMPOL_FLAG_BASE) {
402         return RPMRC_FAIL;
403     }
404
405     if (pt->execsemodule) {
406         if (argvAdd(&pt->semodargs, "-r") < 0 || argvAdd(&pt->semodargs, pol->name) < 0) {
407             rc = RPMRC_FAIL;
408         }
409     } else {
410         if (semanage_module_remove(pt->sh, (char *) pol->name) < 0) {
411             rc = RPMRC_FAIL;
412         }
413     }
414
415     if (rc != RPMRC_OK) {
416         rpmlog(RPMLOG_ERR, _("Failed to remove policy module: %s\n"),
417                pol->name);
418     } else {
419         pt->changes++;
420     }
421
422     return rc;
423 }
424
425 static rpmRC sepoltransCommit(sepoltrans * pt)
426 {
427     rpmRC rc = RPMRC_OK;
428
429     if (pt->changes == 0) {
430         return rc;
431     }
432
433     if (pt->execsemodule) {
434         int status;
435         pid_t pid = fork();
436         int fd;
437
438         switch (pid) {
439         case -1:
440             rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
441                    strerror(errno));
442             rc = RPMRC_FAIL;
443             break;
444         case 0:
445             fd = open("/dev/null", O_RDWR);
446             dup2(fd, STDIN_FILENO);
447             dup2(fd, STDOUT_FILENO);
448             dup2(fd, STDERR_FILENO);
449             execv(pt->semodulepath, pt->semodargs);
450             rpmlog(RPMLOG_ERR, _("Failed to execute %s: %s\n"),
451                    pt->semodulepath, strerror(errno));
452             exit(1);
453         default:
454             waitpid(pid, &status, 0);
455             if (!WIFEXITED(status)) {
456                 rpmlog(RPMLOG_ERR, _("%s terminated abnormally\n"),
457                        pt->semodulepath);
458                 rc = RPMRC_FAIL;
459             } else if (WEXITSTATUS(status)) {
460                 rpmlog(RPMLOG_ERR, _("%s failed with exit code %i\n"),
461                        pt->semodulepath, WEXITSTATUS(status));
462                 rc = RPMRC_FAIL;
463             }
464         }
465     } else {
466         if (semanage_commit(pt->sh) < 0) {
467             rpmlog(RPMLOG_ERR, _("Failed to commit policy changes\n"));
468             rc = RPMRC_FAIL;
469         }
470     }
471
472     return rc;
473 }
474
475 static rpmRC sepolRelabelFiles(void)
476 {
477     rpmRC rc = RPMRC_OK;
478     pid_t pid;
479     int fd;
480     int status;
481     char *restoreconPath = rpmExpand("%{__restorecon}", NULL);
482
483     if (!restoreconPath) {
484         rpmlog(RPMLOG_ERR, _("Failed to expand restorecon path"));
485         return RPMRC_FAIL;
486     }
487
488     /* execute restorecon -R / */
489     pid = fork();
490     switch (pid) {
491     case -1:
492         rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
493                strerror(errno));
494         rc = RPMRC_FAIL;
495         break;
496     case 0:
497         fd = open("/dev/null", O_RDWR);
498         dup2(fd, STDIN_FILENO);
499         dup2(fd, STDOUT_FILENO);
500         dup2(fd, STDERR_FILENO);
501         execl(restoreconPath, "restorecon", "-R", "/", NULL);
502         rpmlog(RPMLOG_ERR, _("Failed to execute %s: %s\n"), restoreconPath,
503                strerror(errno));
504         exit(1);
505     default:
506         waitpid(pid, &status, 0);
507         if (!WIFEXITED(status)) {
508             rpmlog(RPMLOG_ERR, _("%s terminated abnormally\n"),
509                    restoreconPath);
510             rc = RPMRC_FAIL;
511         } else if (WEXITSTATUS(status)) {
512             rpmlog(RPMLOG_ERR, _("%s failed with exit code %i\n"),
513                    restoreconPath, WEXITSTATUS(status));
514             rc = RPMRC_FAIL;
515         }
516     }
517
518     _free(restoreconPath);
519
520     return rc;
521 }
522
523 static rpmRC sepolGo(void)
524 {
525     semanage_handle_t *sh;
526     int existingPolicy;
527     char *policytype = NULL;
528     rpmRC rc = RPMRC_FAIL;
529
530     static int performed = 0;
531     if (performed) {
532         return RPMRC_OK;
533     }
534     performed = 1;
535
536     if (rpmChrootIn()) {
537         goto exit;
538     }
539
540     if (selinux_getpolicytype(&policytype) < 0) {
541         goto exit;
542     }
543
544     sepolPreparePolicies(policiesHead, policytype);
545
546     /* determine if this is the first time installing policy */
547     sh = semanage_handle_create();
548     existingPolicy = (semanage_is_managed(sh) == 1);
549     semanage_handle_destroy(sh);
550
551     /* now load the policies */
552     rc = sepolLoadPolicies(policiesHead);
553
554     /* re-init selinux and re-read the files contexts, since things may have changed */
555     selinux_reset_config();
556     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
557         if (rpmtsSELabelInit(ts, 0) == RPMRC_OK) {
558             /* if this was the first time installing policy, every package before
559              * policy was installed will be mislabeled (e.g. semodule). So, relabel
560              * the entire filesystem if this is the case */
561             if (!existingPolicy) {
562                 if (sepolRelabelFiles() != RPMRC_OK) {
563                     rpmlog(RPMLOG_WARNING, _("Failed to relabel filesystem. Files may be mislabeled\n"));
564                 }
565             }
566         } else {
567             rpmlog(RPMLOG_WARNING, _("Failed to reload file contexts. Files may be mislabeled\n"));
568         }
569     }
570
571   exit:
572     if (rpmChrootOut()) {
573         rc = RPMRC_FAIL;
574     }
575
576     _free(policytype);
577
578     return rc;
579 }
580
581 static rpmRC sepolAddTE(rpmte te)
582 {
583     sepol *pol;
584     sepol *polTail;
585
586     if (!rpmteHasCollection(te, name)) {
587         return RPMRC_OK;
588     }
589
590     pol = sepolNew(te);
591     if (!pol) {
592         /* something's wrong with the policy information, either missing or
593          * corrupt. abort */
594         rpmlog(RPMLOG_ERR, _("Failed to extract policy from %s\n"),
595                rpmteNEVRA(te));
596         return RPMRC_FAIL;
597     }
598
599     /* find the tail of pol */
600     polTail = pol;
601     while (polTail->next) {
602         polTail = polTail->next;
603     }
604
605     /* add the new policy to the list */
606     if (!policiesHead) {
607         policiesHead = pol;
608         policiesTail = polTail;
609     } else {
610         if (rpmteType(te) == TR_ADDED) {
611             /* add to the end of the list */
612             policiesTail->next = pol;
613             policiesTail = polTail;
614         } else {
615             /* add to the beginning of the list */
616             polTail->next = policiesHead;
617             policiesHead = pol;
618         }
619     }
620
621     return RPMRC_OK;
622 }
623
624
625
626 rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *_name, const char *_opts)
627 {
628     ts = _ts;
629     name = strdup(_name);
630     policiesHead = policiesTail = NULL;
631     return RPMRC_OK;
632 }
633
634 rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
635 {
636     _free(name);
637     ts = NULL;
638     policiesHead = policiesTail = sepolFree(policiesHead);
639     return RPMRC_OK;
640 }
641
642 rpmRC PLUGINHOOK_OPENTE_FUNC(rpmte te)
643 {
644     return sepolAddTE(te);
645 }
646
647 rpmRC PLUGINHOOK_COLL_POST_ADD_FUNC(void)
648 {
649     return sepolGo();
650 }
651
652 rpmRC PLUGINHOOK_COLL_PRE_REMOVE_FUNC(void)
653 {
654     return sepolGo();
655 }