066e2a7bc1b4e6826d8f04bee9a310473cd6a7f1
[framework/security/smack.git] / utils / smackd.c
1 /*
2  * This file is part of libsmack
3  *
4  * Copyright (C) 2011 Intel Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * version 2.1 as published by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  * Authors:
21  * Brian McGillion <brian.mcgillion@intel.com>
22  */
23
24 #include "common.h"
25 #include <signal.h>
26 #include <syslog.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <limits.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/inotify.h>
35 #include <sys/select.h>
36
37 #define PID_FILE "/var/run/smackd.pid"
38 #define BUF_SIZE (4 * (sizeof(struct inotify_event) + NAME_MAX + 1))
39
40 #define ACCESS_FD 0
41 #define CIPSO_FD 1
42
43 int notify_handles[2];
44 static volatile sig_atomic_t terminate = 0;
45 static volatile sig_atomic_t restart = 0;
46
47 enum mask_action {
48         CREATE,
49         MODIFY,
50         DELETE
51 };
52
53 static void clear_all_rules()
54 {
55         if (clear() == -1)
56                 syslog(LOG_ERR, "Failed to clear all rules");
57 }
58
59 static void load_all_rules()
60 {
61         if (apply_rules(ACCESSES_D_PATH, 0))
62                 syslog(LOG_DEBUG, "Failed to load all rules");
63 }
64
65 static void signal_handler(int sig)
66 {
67         switch (sig) {
68         case SIGTERM:
69                 terminate = 1;
70                 break;
71         case SIGHUP:
72                 restart = 1;
73                 break;
74         default:
75                 syslog(LOG_DEBUG, "Unrequested signal : %d", sig);
76                 break;
77         }
78 }
79
80 static int lockPidFile()
81 {
82         int fd;
83         struct flock lock;
84         char buf[BUF_SIZE];
85
86         fd = open(PID_FILE, O_RDWR | O_CREAT | O_CLOEXEC,
87                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
88         if (fd < 0) {
89                 syslog(LOG_ERR, "Failed to open (%s) : %m", PID_FILE);
90                 return -1;
91         }
92
93         lock.l_len = 0;
94         lock.l_start = 0;
95         lock.l_type = F_WRLCK;
96         lock.l_whence = SEEK_SET;
97
98         if (fcntl(fd, F_SETLK, &lock) < 0) {
99                 if (errno == EACCES || errno == EAGAIN) {
100                         syslog(LOG_ERR, "Daemon is already running (%s) : %m", PID_FILE);
101                 }
102                 else
103                         syslog(LOG_ERR, "Could not lock PID_FILE (%s) : %m", PID_FILE);
104
105                 close(fd);
106                 return -1;
107         }
108
109         if (ftruncate(fd, 0) < 0) {
110                 syslog(LOG_ERR, "Could not truncate PID_FILE (%s) : %m", PID_FILE);
111                 close(fd);
112                 return -1;
113         }
114
115         snprintf(buf, BUF_SIZE, "%ld\n", (long)getpid());
116         if (write(fd, buf, strlen(buf)) != strlen(buf)) {
117                 syslog(LOG_ERR, "Could not write to PID_FILE (%s) : %m", PID_FILE);
118                 close(fd);
119                 return -1;
120         }
121
122         return fd;
123 }
124
125 static int daemonize()
126 {
127         int maxfd, fd;
128
129         switch (fork()) {
130         case -1:
131                 syslog(LOG_ERR, "Failed to fork : %m");
132                 return -1;
133         case 0:
134                 break;
135         default:
136                 exit(EXIT_SUCCESS);
137         }
138
139         if (setsid() < 0)
140                 return -1;
141
142         //do not regain a terminal
143         switch (fork()) {
144         case -1:
145                 syslog(LOG_ERR, "Failed to fork (2) : %m");
146                 return -1;
147         case 0:
148                 break;
149         default:
150                 exit(EXIT_SUCCESS);
151         }
152
153         umask(0);
154
155         if (chdir("/") < 0)
156                 syslog(LOG_ERR, "Failed to chdir '/' : %m");
157
158         maxfd = sysconf(_SC_OPEN_MAX);
159         maxfd = maxfd != -1 ? maxfd : 4096;
160
161         for (fd = 0; fd < maxfd; fd++)
162                 close(fd);
163
164         if (!freopen("/dev/null", "r", stdin))
165                 syslog(LOG_DEBUG, "Failed to reopen stdin : %m");
166         if(!freopen("/dev/null", "w", stdout))
167                 syslog(LOG_DEBUG, "Failed to reopen stout : %m");
168         if(!freopen("/dev/null", "w", stderr))
169                 syslog(LOG_DEBUG, "Failed to reopen sterr : %m");
170
171         return lockPidFile();
172 }
173
174 static int configure_inotify()
175 {
176         int inotifyFd, fd;
177
178         inotifyFd = inotify_init();
179         if (inotifyFd < 0) {
180                 syslog(LOG_ERR, "Failed to init inotify : %m");
181                 return -1;
182         }
183
184         fd = inotify_add_watch(inotifyFd, ACCESSES_D_PATH,
185                                IN_DELETE | IN_CLOSE_WRITE | IN_MOVE);
186         if (fd < 0) {
187                 syslog(LOG_ERR, "Failed to inotify_add_watch (%s) : %m",
188                        ACCESSES_D_PATH);
189                 return -1;
190         }
191
192         notify_handles[ACCESS_FD] = fd;
193
194         fd = inotify_add_watch(inotifyFd, CIPSO_D_PATH,
195                                IN_DELETE | IN_CLOSE_WRITE | IN_MOVE);
196         if (fd < 0) {
197                 syslog(LOG_ERR, "Failed to inotify_add_watch (%s) : %m",
198                        CIPSO_D_PATH);
199                 return -1;
200         }
201
202         notify_handles[CIPSO_FD] = fd;
203
204         return inotifyFd;
205 }
206
207 static void modify_access_rules(char *file, enum mask_action action)
208 {
209         char path[PATH_MAX];
210         int ret;
211
212         sprintf(path,"%s/%s", ACCESSES_D_PATH, file);
213
214         if (action == CREATE)
215                 ret = apply_rules(path, 0);
216         else if (action == MODIFY) {
217                 ret = apply_rules(path, 1);
218                 ret = apply_rules(path, 0);
219         }
220
221         if (ret)
222                 syslog(LOG_ERR, "Failed load access rules (%s), action (%d) :%m",
223                        path, action);
224 }
225
226 static void modify_cipso_rules(char *file)
227 {
228         char path[PATH_MAX];
229         sprintf(path,"%s/%s", CIPSO_D_PATH, file);
230
231         if (apply_cipso(path))
232                 syslog(LOG_ERR, "Failed to load cipso rules (%s) : %m", path);
233 }
234
235 static int handle_inotify_event(int inotifyFd)
236 {
237         struct inotify_event *event;
238         char buf[BUF_SIZE];
239         ssize_t num_read;
240         char *head;
241         enum mask_action action;
242         int del = 0;
243         int size = sizeof(struct inotify_event);
244
245         num_read = read(inotifyFd, buf, BUF_SIZE);
246         if (num_read <= 0) {
247                 syslog(LOG_ERR, "Error reading inotify event : %m");
248                 return -1;
249         }
250
251         for (head = buf; head < buf + num_read; head += size + event->len) {
252                 event = (struct inotify_event *) head;
253
254                 if (event->mask & IN_MOVED_TO)
255                         action = CREATE;
256                 else if (event->mask & IN_CLOSE_WRITE)
257                         action = MODIFY;
258                 else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
259                         del = 1;
260                         continue;
261                 }
262
263                 if (event->wd == notify_handles[ACCESS_FD])
264                         modify_access_rules(event->name, action);
265                 else if (event->wd == notify_handles[CIPSO_FD])
266                         modify_cipso_rules(event->name);
267         }
268
269         if (del) {
270                 //at least one file was removed so we should reparse the rules
271                 clear_all_rules();
272                 load_all_rules();
273         }
274
275         return 0;
276 }
277
278 static int monitor(int inotifyFd)
279 {
280         fd_set readSet;
281         FD_ZERO(&readSet);
282         FD_SET(inotifyFd, &readSet);
283
284         return select(inotifyFd + 1, &readSet, NULL, NULL, NULL);
285 }
286
287 void main(int argc, char **argv)
288 {
289         struct sigaction sa;
290         int inotify_fd;
291         int ret;
292         int pid_fd;
293
294         sigemptyset(&sa.sa_mask);
295         sa.sa_handler = signal_handler;
296         sa.sa_flags = SA_RESTART;
297
298         if (sigaction(SIGHUP, &sa, NULL) < 0) {
299                 syslog(LOG_ERR, "failed to listen for signal SIGHUP : %m");
300                 exit(EXIT_FAILURE);
301         }
302
303         if (sigaction(SIGTERM, &sa, NULL) < 0) {
304                 syslog(LOG_ERR, "failed to listen for signal SIGTERM : %m");
305                 exit(EXIT_FAILURE);
306         }
307
308         pid_fd = daemonize();
309         if (pid_fd < 0)
310                 exit(EXIT_FAILURE);
311
312         clear_all_rules();
313         load_all_rules();
314
315         inotify_fd = configure_inotify();
316
317         while (inotify_fd >= 0 && !terminate && !restart) {
318                 ret = monitor(inotify_fd);
319                 if (ret < 0 && errno == EINTR) {
320                         continue;
321                 }
322                 else if (ret < 0) {
323                         syslog(LOG_ERR, "Failed to monitor properly : %m");
324                         break;
325                 }
326
327                 ret = handle_inotify_event(inotify_fd);
328                 if (ret < 0)
329                         break;
330         }
331
332         close(pid_fd);
333         remove(PID_FILE);
334
335         if (restart && execv(argv[0], argv))
336                 syslog(LOG_ERR, "Failed to restart : %m");
337
338         clear_all_rules();
339
340         syslog(LOG_DEBUG, "Finished %s", argv[0]);
341         exit(terminate == 1 ? EXIT_SUCCESS : EXIT_FAILURE);
342 }