ff447ebf5a3c68f3102c775b864c1fe6cd1e8f31
[platform/core/security/security-manager.git] / src / server / service / smack-labels.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Rafal Krypa <r.krypa@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18 /**
19  * @file        smack-labels.cpp
20  * @author      Jan Cybulski <j.cybulski@samsung.com>
21  * @author      Rafal Krypa <r.krypa@samsung.com>
22  * @version     1.0
23  * @brief       Implementation of functions managing smack labels
24  *
25  */
26
27 #include <sys/stat.h>
28 #include <sys/smack.h>
29 #include <sys/xattr.h>
30 #include <linux/xattr.h>
31 #include <memory>
32 #include <fts.h>
33 #include <cstring>
34 #include <string>
35
36 #include <dpl/log/log.h>
37 #include <smack-common.h>
38
39 #include "security-manager.h"
40 #include "smack-labels.h"
41
42 namespace SecurityManager {
43
44 /* Const defined below is used to label links to executables */
45 const char *const LABEL_FOR_PUBLIC_APP_PATH = "User";
46
47 enum class FileDecision {
48     SKIP = 0,
49     LABEL = 1,
50     ERROR = -1
51 };
52
53 typedef std::function<FileDecision(const FTSENT*)> LabelDecisionFn;
54
55 static FileDecision labelAll(const FTSENT *ftsent __attribute__((unused)))
56 {
57     return FileDecision::LABEL;
58 }
59
60 static FileDecision labelDirs(const FTSENT *ftsent)
61 {
62     // label only directories
63     if (S_ISDIR(ftsent->fts_statp->st_mode))
64         return FileDecision::LABEL;
65     return FileDecision::SKIP;
66 }
67
68 static FileDecision labelExecs(const FTSENT *ftsent)
69 {
70     // LogDebug("Mode = " << ftsent->fts_statp->st_mode); // this could be helpfull in debugging
71     // label only regular executable files
72     if (S_ISREG(ftsent->fts_statp->st_mode) && (ftsent->fts_statp->st_mode & S_IXUSR))
73         return FileDecision::LABEL;
74     return FileDecision::SKIP;
75 }
76
77 static FileDecision labelLinksToExecs(const FTSENT *ftsent)
78 {
79     struct stat buf;
80
81     // check if it's a link
82     if ( !S_ISLNK(ftsent->fts_statp->st_mode))
83         return FileDecision::SKIP;
84
85     std::unique_ptr<char, std::function<void(void*)>> target(realpath(ftsent->fts_path, NULL), free);
86
87     if (!target.get()) {
88         LogError("Getting link target for " << ftsent->fts_path << " failed (Error = " << strerror(errno) << ")");
89         return FileDecision::ERROR;
90     }
91
92     if (-1 == stat(target.get(), &buf)) {
93         LogError("stat failed for " << target.get() << " (Error = " << strerror(errno) << ")");
94         return FileDecision::ERROR;
95     }
96     // skip if link target is not a regular executable file
97     if (buf.st_mode != (buf.st_mode | S_IXUSR | S_IFREG)) {
98         // LogDebug(target.get() << "is not a regular executable file. Skipping.");
99         return FileDecision::SKIP;
100     }
101
102     return FileDecision::LABEL;
103 }
104
105 static bool dirSetSmack(const std::string &path, const std::string &label,
106         const char *xattr_name, LabelDecisionFn fn)
107 {
108     char *const path_argv[] = {const_cast<char *>(path.c_str()), NULL};
109     FTSENT *ftsent;
110     FileDecision ret;
111
112     std::unique_ptr<FTS, std::function<void(FTS*)> > fts(
113             fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL),
114             fts_close);
115
116     if (fts.get() == NULL) {
117         LogError("fts_open failed.");
118         return false;
119     }
120
121     while ((ftsent = fts_read(fts.get())) != NULL) {
122         /* Check for error (FTS_ERR) or failed stat(2) (FTS_NS) */
123         if (ftsent->fts_info == FTS_ERR || ftsent->fts_info == FTS_NS) {
124             LogError("FTS_ERR error or failed stat(2) (FTS_NS)");
125             return false;
126         }
127
128         /* avoid to tag directories two times */
129         if (ftsent->fts_info == FTS_D)
130             continue;
131
132         ret = fn(ftsent);
133         if (ret == FileDecision::ERROR) {
134             LogError("fn(ftsent) failed.");
135             return false;
136         }
137
138         if (ret == FileDecision::LABEL) {
139             if (lsetxattr(ftsent->fts_path, xattr_name, label.c_str(), label.length(), 0) != 0) {
140                 LogError("lsetxattr failed.");
141                 return false;
142             }
143         }
144
145     }
146
147     /* If last call to fts_read() set errno, we need to return error. */
148     if ((errno != 0) && (ftsent == NULL)) {
149         LogError("Last errno from fts_read: " << strerror(errno));
150         return false;
151     }
152     return true;
153 }
154
155
156 static bool labelDir(const std::string &path, const std::string &label,
157         bool set_transmutable, bool set_executables)
158 {
159     bool ret = true;
160
161     // setting access label on everything in given directory and below
162     ret = dirSetSmack(path, label, XATTR_NAME_SMACK, labelAll);
163     if (!ret) {
164         LogError("dirSetSmack failed (access label)");
165         return ret;
166     }
167
168     if (set_transmutable) {
169         // setting transmute on dirs
170         ret = dirSetSmack(path, "TRUE", XATTR_NAME_SMACKTRANSMUTE, labelDirs);
171         if (!ret) {
172             LogError("dirSetSmack failed (transmute)");
173             return ret;
174         }
175     }
176
177     if (set_executables) {
178         ret = dirSetSmack(path, label, XATTR_NAME_SMACKEXEC, &labelExecs);
179         if (!ret)
180         {
181             LogError("dirSetSmack failed (execs).");
182             return ret;
183         }
184
185         //setting execute label for everything with permission to execute
186         ret = dirSetSmack(path, label, XATTR_NAME_TIZENEXEC, &labelLinksToExecs);
187         if (!ret)
188         {
189             LogError("dirSetSmack failed (link to execs).");
190             return ret;
191         }
192     }
193
194     return ret;
195 }
196
197 bool setupPath(const std::string &pkgId, const std::string &path,
198     app_install_path_type pathType)
199 {
200     std::string label;
201     bool label_executables, label_transmute;
202
203     switch (pathType) {
204     case SECURITY_MANAGER_PATH_PRIVATE:
205         if (!generateAppLabel(pkgId, label))
206             return false;
207         label_executables = true;
208         label_transmute = false;
209         break;
210     case SECURITY_MANAGER_PATH_PUBLIC:
211         label.assign(LABEL_FOR_PUBLIC_APP_PATH);
212         label_executables = false;
213         label_transmute = true;
214         break;
215     case SECURITY_MANAGER_PATH_PUBLIC_RO:
216         label.assign("_");
217         label_executables = false;
218         label_transmute = false;
219         break;
220     default:
221         LogError("Path type not known.");
222         return false;
223     }
224     return labelDir(path, label, label_transmute, label_executables);
225 }
226
227 } // namespace SecurityManager