├── Makefile
├── Readme.md
├── flow.png
└── pindown.c
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile for the kernel security code
3 | #
4 | obj-$(CONFIG_KEYS) += keys/
5 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux
6 |
7 | # if we don't select a security model, use the default capabilities
8 | ifneq ($(CONFIG_SECURITY),y)
9 | obj-y += commoncap.o
10 | endif
11 |
12 | # Object file lists
13 | obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o
14 | # Must precede capability.o in order to stack properly.
15 | obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
16 | obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
17 | obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
18 |
19 | obj-m += pindown.o
20 |
21 | all:
22 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
23 |
24 | clean:
25 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
26 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 | This project uses LSM hooks to enforce a simple access control policy. The implementation associates the
3 | path of a binary file which is allowed to access a protected file. The path of the binary file is stored in the extended attribute
4 | (XAttr) of the inode of the protected file. When a process tries to access this file, the path of the binary which was
5 | loaded during the exec() call of the process is checked against the XAttr attribute ("security.pindown" in this case). If the
6 | path matches then the process is allowed to access the file. Otherwise, access is denied.
7 |
8 | #### PinDOWN LSM implementation
9 | Pindown module that implements the four hooks listed below which are exposed by the Linux Security Modules framework:
10 | * task_alloc_security
11 | * task_free_security
12 | * bprm_set_security
13 | * inode_permission
14 |
15 | This implementation is tested for the linux kernel version 2.6.23. The diagram below shows how the implementation control
16 | access to files using LSM hooks.
17 | 
18 |
19 | #### Commands to load the module
20 | * Download the kernel linux-2.6.23. Older distros of linux such as ubuntu 8.04 LTS can be used because it allows loading kernel modules without reboot.
21 | * Copy `pindown.c` and `Makefile` to linux-2.6.23/security directory.
22 | * Run `make all` in the root directory of the kernel repository. This generates the kernel object `security/pindown.ko`.
23 | * To insert the module, run `insmod security/pindown.ko`. The hooks provided in the module should enforce access control.
24 | * Logs can be accessed in the file `/var/log/kern.log`. Check if the modules is loaded using `lsmod` command.
25 |
26 | #### Setting the access control for policy
27 | Following example limits the acces to file `/foo/bar/protected_file` to the program `/usr/bin/vi` only.
28 | Any other program, trying to access the file would be denied access to the protected file.
29 | * Set the XAttr attribute on the protected file.
30 | `sudo setfattr -n security.pindown -v '/usr/bin/vi' /foo/bar/protected_file`
31 | * Check the XAttr attribute
32 | `sudo getfattr -n security.pindown /foo/bar/protected_file`
33 | * Load the `pindown.ko` module as shown in the previous section.
34 |
35 | With that, only `vi` program should be have access to the file. Any other program attempting to access the file would be denied the permission.
36 |
37 | #### References
38 | The project is inspired from the [PinUP](https://enck.org/pubs/acsac08a.pdf) paper.
39 | [Linux Security Module Framework](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.5163&rep=rep1&type=pdf)
--------------------------------------------------------------------------------
/flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/node3/Linux-Security-Module/94ee572d4abe71c2234072a8baedea1ab5d0aa14/flow.png
--------------------------------------------------------------------------------
/pindown.c:
--------------------------------------------------------------------------------
1 | /**
2 | * PinDOWN implementation
3 | */
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | #define MAX_PATHLEN 128
44 |
45 | typedef struct pindown_security_t {
46 | char bprm_pathname[MAX_PATHLEN];
47 | u32 pathlen;
48 | } pindown_security_t;
49 |
50 |
51 | MODULE_LICENSE("GPL");
52 |
53 | #define INITCONTEXTLEN 100
54 | #define XATTR_SAMPLE_SUFFIX "pindown"
55 | #define XATTR_NAME_SAMPLE XATTR_SECURITY_PREFIX XATTR_SAMPLE_SUFFIX
56 |
57 | extern struct security_operations *security_ops;
58 |
59 | /* Function: get_inode_policy(@inode, @name)
60 | * Description:
61 | * - Utility function for getting the pathname policy from an inode
62 | * - returns pointer to allocated string (needs deallocation)
63 | * Input:
64 | * @inode : the inode
65 | * @name : xattr name to lookup
66 | * Output:
67 | * - returns allocated string of pathname
68 | * - NULL indicates error or not found
69 | * - return value needs to be kfree()'d after this call if not NULL
70 | */
71 | char *get_inode_policy(struct inode *inode, const char *name)
72 | {
73 | int rc = -1;
74 | char *pathname = NULL;
75 | struct dentry *dentry;
76 | int len = 0;
77 |
78 | printk(KERN_INFO "PinDOWN: get_inode_policy.\n");
79 | /* Make sure this inode supports the functions we need */
80 | if (!inode || !inode->i_op || !inode->i_op->getxattr) {
81 | goto out;
82 | }
83 |
84 | /* getxattr requires a dentry */
85 | dentry = d_find_alias(inode);
86 | if (!dentry) {
87 | goto out;
88 | }
89 |
90 | /* Try default length */
91 | len = INITCONTEXTLEN;
92 | pathname = (char*)kmalloc(sizeof(char)*len, GFP_KERNEL);
93 | if (!pathname) {
94 | dput(dentry);
95 | goto out;
96 | }
97 | rc = inode->i_op->getxattr(dentry, name, pathname, len*sizeof(char));
98 |
99 | if (rc == -ERANGE) {
100 | /* Need a larger buffer. Query for the right size */
101 | rc = inode->i_op->getxattr(dentry, name, NULL, 0);
102 | if (rc < 0) { /* could not get size */
103 | dput(dentry);
104 | kfree(pathname);
105 | pathname = NULL;
106 | goto out;
107 | }
108 |
109 | /* start over with correct size */
110 | kfree(pathname);
111 | len = rc / sizeof(char);
112 | pathname = (char*)kmalloc(sizeof(char)*len, GFP_KERNEL);
113 | if (!pathname) {
114 | rc = -ENOMEM;
115 | dput(dentry);
116 | goto out;
117 | }
118 | rc = inode->i_op->getxattr(dentry, name, pathname, len*sizeof(char));
119 | }
120 | dput(dentry);
121 |
122 | if (rc < 0) {
123 | kfree(pathname);
124 | pathname = NULL;
125 | goto out;
126 | }
127 |
128 | out:
129 | return pathname;
130 | }
131 |
132 | /* Function: pindown_inode_permission(@inode, @mask, @nd)
133 | * Description:
134 | * - LSM Hook .inode_permission()
135 | * - Performs the main access control check on files
136 | * Input:
137 | * @inode : pointer to the inode (object) of the lookup
138 | * @mask : permission mask of the lookup (not used at all)
139 | * @nd : ?? (not used at all)
140 | * Output:
141 | * - returns 0 for access granted, -EACCES for permission denied
142 | */
143 | int pindown_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
144 | {
145 |
146 | //Initial default allow. Change to default deny once implemented.
147 | int rc = 0;
148 | pindown_security_t *sec = NULL;
149 | char *inode_policy = NULL;
150 | int i;
151 |
152 | /* Don't check this if it is a directory */
153 | if ((inode->i_mode & S_IFMT) == S_IFDIR) {
154 | rc = 0;
155 | //printk(KERN_INFO "PinDOWN: pindown_inode_permission: Don't check this if it is a directory.\n");
156 | goto out;
157 | }
158 |
159 | /* Get the inode policy */
160 | inode_policy = get_inode_policy(inode, "security.pindown");
161 | if (!inode_policy) {
162 | rc = 0;
163 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: No policy, allowing access.\n");
164 | goto out;
165 | }
166 |
167 | /* Get the process security info */
168 | sec = current->security;
169 | if (!sec || sec->bprm_pathname[0] == '\0') {
170 | // Not allowing a program to access a file if the program has not set the security field
171 | rc = -EACCES;
172 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: Not allowing a program to access a file if the program has not set the security field.\n");
173 | goto out;
174 | }
175 |
176 | /* Compare process security info to inode policy */
177 | rc = 0;
178 | i=0;
179 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: inode_policy:%s.\n", inode_policy);
180 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: sec->bprm_pathname:%s.\n", sec->bprm_pathname);
181 | while ((i < MAX_PATHLEN) && (inode_policy[i] != '\0') && (sec->bprm_pathname[i] != '\0')) {
182 | if (inode_policy[i] != sec->bprm_pathname[i]) {
183 | rc = -EACCES;
184 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: pathname and filename mismatch.\n");
185 | goto out;
186 | }
187 | i++;
188 | }
189 |
190 | if ((i <= MAX_PATHLEN) && (inode_policy[i] == sec->bprm_pathname[i]) && (inode_policy[i] == '\0')) {
191 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: allowed.\n");
192 | rc = 0;
193 | }
194 | else {
195 | // printk(KERN_INFO "PinDOWN: pindown_inode_permission: denied.\n");
196 | rc = -EACCES;
197 | }
198 |
199 | out:
200 | if (inode_policy) {
201 | kfree(inode_policy);
202 | }
203 | return rc;
204 | }
205 |
206 |
207 | /* Function: pindown_task_alloc_security(@p)
208 | * Description:
209 | * - LSM Hook .task_alloc_security()
210 | * - Allocates @p->security to store the path
211 | * Input:
212 | * @p : pointer to the child task_struct
213 | * Output:
214 | * - @p->security is allocated
215 | * - returns 0 if successful
216 | */
217 | int pindown_task_alloc_security(struct task_struct * p)
218 | {
219 | int err = 0;
220 | int i;
221 | pindown_security_t *sec = NULL;
222 | pindown_security_t *parent_sec = NULL; // Parent
223 |
224 | printk(KERN_INFO "PinDOWN: pindown_task_alloc_security.\n");
225 | sec = (pindown_security_t *)kmalloc(sizeof(pindown_security_t), GFP_KERNEL);
226 | if (sec == NULL) {
227 | err = -ENOMEM;
228 | goto out;
229 | }
230 |
231 | /* When we fork, we are still the same application as our
232 | * parent, therefore, it is appropriate to copy the
233 | * parent's digest. On exec(), the digest will be set to
234 | * the new application binary with pindown_bprm_set_security()
235 | */
236 |
237 | parent_sec = current->security;
238 | if (!parent_sec) {
239 | sec->bprm_pathname[0] = '\0';
240 | sec->pathlen = 0;
241 | }
242 | else {
243 | //printk(KERN_INFO "PinDOWN: PARENT %s.\n", parent_sec->bprm_pathname);
244 | i = 0;
245 | while ((i < MAX_PATHLEN) && (parent_sec->bprm_pathname[i] != '\0')) {
246 | sec->bprm_pathname[i] = parent_sec->bprm_pathname[i];
247 | i++;
248 | }
249 | if (i <= MAX_PATHLEN) {
250 | sec->pathlen = i;
251 | }
252 | else {
253 | err = -ENOMEM;
254 | // printk(KERN_INFO "PinDOWN: PATHLEN is too big to store\n");
255 | goto out;
256 | }
257 | }
258 | p->security = sec;
259 | out:
260 | return err;
261 | }
262 |
263 | /* Function: pindown_task_free_security(@p)
264 | * Description:
265 | * - LSM Hook .task_free_security()
266 | * - Deallocates @p->security
267 | * Input:
268 | * @p : pointer to the child task_struct
269 | * Output:
270 | * - @p->security is deallocated
271 | */
272 | void pindown_task_free_security(struct task_struct * p)
273 | {
274 | pindown_security_t *sec;
275 | printk(KERN_INFO "PinDOWN: pindown_task_free_security.\n");
276 | if (!p->security) {
277 | return;
278 | }
279 | sec = p->security;
280 | kfree(sec);
281 | p->security = NULL;
282 | return;
283 | }
284 |
285 | /* Function: pindown_bprm_set_security(@bprm)
286 | * Description:
287 | * - LSM Hook .bprm_set_security()
288 | * - Sets @current->security to the path of the binary
289 | * Input:
290 | * @bprm : pointer to a binary being loaded by the kernel
291 | * Output:
292 | * - @current->security is set to the path of the binary
293 | * - return 0 if the hook is successful and permission is granted
294 | */
295 | int pindown_bprm_set_security(struct linux_binprm * bprm)
296 | {
297 | int rc = 0;
298 | int i;
299 | pindown_security_t *sec = current->security;
300 |
301 | printk(KERN_INFO "PinDOWN: pindown_bprm_set_security.\n");
302 | if (sec == NULL) {
303 | rc = pindown_task_alloc_security(current);
304 | }
305 |
306 | /* Set the pathname from the exec()'d binary filename */
307 | if (!rc) {
308 | if (!bprm || !bprm->filename) {
309 | sec->bprm_pathname[0] = '\0';
310 | sec->pathlen = 0;
311 | }
312 | else {
313 | // printk(KERN_INFO "PinDOWN: BINARY %s.\n", bprm->filename);
314 | i = 0;
315 | while ((i < MAX_PATHLEN) && (bprm->filename[i] != '\0')) {
316 | sec->bprm_pathname[i] = bprm->filename[i];
317 | i++;
318 | }
319 | while (i < MAX_PATHLEN) {
320 | sec->bprm_pathname[i] = '\0';
321 | i++;
322 | }
323 | sec->pathlen = i;
324 | // printk(KERN_INFO "PinDOWN: SAVED %d %s.\n",sec->pathlen ,sec->bprm_pathname);
325 | }
326 | }
327 | else {
328 | //printk(KERN_INFO "PinDOWN: Should I do something if error was returned by pindown_task_alloc_security() ??");
329 | }
330 | return rc;
331 | }
332 |
333 |
334 | static struct security_operations pindown_ops = {
335 | .task_alloc_security = pindown_task_alloc_security,
336 | .task_free_security = pindown_task_free_security,
337 | .bprm_set_security = pindown_bprm_set_security,
338 | .inode_permission = pindown_inode_permission,
339 | };
340 |
341 | static __init int pindown_init(void)
342 | {
343 | if (register_security (&pindown_ops)) {
344 | printk("PinDOWN: Unable to register with kernel.\n");
345 | return 0;
346 | }
347 | printk(KERN_INFO "PinDOWN: Initializing.\n");
348 | return 0;
349 | }
350 |
351 | static __exit void pindown_exit(void)
352 | {
353 | printk(KERN_INFO "PinDOWN: Exiting.\n");
354 | unregister_security(&pindown_ops);
355 | }
356 |
357 | module_init(pindown_init);
358 | module_exit(pindown_exit);
359 |
--------------------------------------------------------------------------------