1 /*
2 * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author:
19 * Alexey Kodanev <alexey.kodanev@oracle.com>
20 *
21 * Test checks following preconditions:
22 *
23 * Symlinks
24 * ---------
25 * Users who own sticky world-writable directory can't follow symlinks
26 * inside that directory if their don't own ones. All other users can follow.
27 *
28 * Hardlinks
29 * ---------
30 * Hard links restriction applies only to non-privileged users. Only
31 * non-privileged user can't create hard links to files if he isn't owner
32 * of the file or he doesn't have write access to the file.
33 */
34
35 #define _GNU_SOURCE
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <pwd.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <signal.h>
44
45 #include "test.h"
46 #include "safe_macros.h"
47
48 char *TCID = "prot_hsymlinks";
49 int TST_TOTAL = 396;
50
51 /* create 3 files and 1 dir in each base dir */
52 #define MAX_FILES_CREATED 4
53 #define MAX_PATH 128
54 #define MAX_CMD_LEN 64
55 #define MAX_USER_NAME 16
56
57 enum {
58 ROOT = 0,
59 TEST_USER,
60 USERS_NUM
61 };
62
63 #define BASE_DIR_NUM (USERS_NUM + 1)
64 /*
65 * max test files and directories
66 * that will be created during the test
67 * is't not include symlinks and hardlinks
68 * and base directories
69 */
70 #define MAX_ENTITIES (MAX_FILES_CREATED * BASE_DIR_NUM)
71
72 struct dir_params {
73 char path[MAX_PATH];
74 int world_writable;
75 int sticky;
76 int owner;
77 };
78
79 static struct dir_params bdirs[BASE_DIR_NUM];
80
81 static const char file_ext[] = ".hs";
82
83 enum {
84 IS_FILE = 0,
85 IS_DIRECTORY,
86 };
87
88 struct user_file {
89 char path[MAX_PATH];
90 int is_dir;
91 };
92
93 struct test_user {
94 char name[MAX_USER_NAME];
95 struct user_file file[MAX_ENTITIES];
96 int num;
97 };
98
99 static struct test_user users[USERS_NUM];
100
101 struct link_info {
102 char path[MAX_PATH];
103 int owner;
104 int source_owner;
105 int in_world_write;
106 int in_sticky;
107 int is_dir;
108 int dir_owner;
109 };
110
111 /* test flags */
112 enum {
113 CANNOT_FOLLOW = -1,
114 CAN_FOLLOW = 0,
115 };
116
117 enum {
118 CANNOT_CREATE = -1,
119 CAN_CREATE = 0,
120 };
121
122 static char *tmp_user_name;
123 static char *default_user = "hsym";
124 static int nflag;
125 static int skip_cleanup;
126
127 static const option_t options[] = {
128 {"u:", &nflag, &tmp_user_name}, /* -u #user name */
129 {"s", &skip_cleanup, NULL},
130 {NULL, NULL, NULL}
131 };
132 /* full length of the test tmpdir path in /tmp */
133 static size_t cwd_offset;
134
135 static const char hrdlink_proc_path[] = "/proc/sys/fs/protected_hardlinks";
136 static const char symlink_proc_path[] = "/proc/sys/fs/protected_symlinks";
137
138 static void help(void);
139 static void setup(int argc, char *argv[]);
140 static void cleanup(void);
141
142 static void test_user_cmd(const char *user_cmd);
143
144 static int disable_protected_slinks;
145 static int disable_protected_hlinks;
146
147 /*
148 * changes links restrictions
149 * @param value can be:
150 * 0 - restrictions is off
151 * 1 - restrictions is on
152 */
153 static void switch_protected_slinks(int value);
154 static void switch_protected_hlinks(int value);
155
156 static int get_protected_slinks(void);
157 static int get_protected_hlinks(void);
158
159 static void create_link_path(char *buffer, int size, const char *path);
160 static int create_check_hlinks(const struct user_file *ufile, int owner);
161 static int create_check_slinks(const struct user_file *ufile, int owner);
162 static int check_symlink(const struct link_info *li);
163 static int try_open(const char *name, int mode);
164 /* try to open symlink in diff modes */
165 static int try_symlink(const char *name);
166
167 static int test_run(void);
168 static void init_base_dirs(void);
169 static void init_files_dirs(void);
170
171 /* change effective user id and group id by name
172 * pass NULL to set root
173 */
174 static void set_user(const char *name);
175
176 /* add new created files to user struct */
177 static void ufiles_add(int usr, char *path, int type);
178
main(int argc,char * argv[])179 int main(int argc, char *argv[])
180 {
181 setup(argc, argv);
182
183 test_run();
184
185 cleanup();
186
187 tst_exit();
188 }
189
setup(int argc,char * argv[])190 static void setup(int argc, char *argv[])
191 {
192 tst_parse_opts(argc, argv, options, &help);
193
194 tst_require_root();
195
196 if (tst_kvercmp(3, 7, 0) < 0)
197 tst_brkm(TCONF, NULL,
198 "Test must be run with kernel 3.7 or newer");
199
200 /* initialize user names */
201 strcpy(users[ROOT].name, "root");
202
203 if (tmp_user_name == NULL)
204 tmp_user_name = default_user;
205 snprintf(users[TEST_USER].name, MAX_USER_NAME, "%s", tmp_user_name);
206
207 tst_sig(FORK, DEF_HANDLER, cleanup);
208
209 test_user_cmd("useradd");
210 /*
211 * enable hardlinks and symlinks restrictions,
212 * it's not defualt but have to check
213 */
214 if (!get_protected_hlinks()) {
215 switch_protected_hlinks(1);
216 disable_protected_hlinks = 1;
217 }
218 if (!get_protected_slinks()) {
219 switch_protected_slinks(1);
220 disable_protected_slinks = 1;
221 }
222
223 tst_tmpdir();
224
225 init_base_dirs();
226
227 init_files_dirs();
228 }
229
test_run(void)230 static int test_run(void)
231 {
232 tst_resm(TINFO, " --- HARDLINKS AND SYMLINKS RESTRICTIONS TEST ---\n");
233
234 int result_slink = 0,
235 result_hlink = 0,
236 usr,
237 file;
238
239 const struct user_file *ufile;
240 /*
241 * create symlinks and hardlinks from each user's files
242 * to each world writable directory
243 */
244 for (usr = 0; usr < USERS_NUM; ++usr) {
245 /* get all users files and directories */
246 for (file = 0; file < users[usr].num; ++file) {
247 ufile = &users[usr].file[file];
248 result_slink |= create_check_slinks(ufile, usr);
249 result_hlink |= create_check_hlinks(ufile, usr);
250 }
251 }
252
253 /* final results */
254 tst_resm(TINFO, "All test-cases have been completed, summary:\n"
255 " - symlinks test:\t%s\n"
256 " - hardlinks test:\t%s",
257 (result_slink == 1) ? "FAIL" : "PASS",
258 (result_hlink == 1) ? "FAIL" : "PASS");
259
260 return result_slink | result_hlink;
261 }
262
cleanup(void)263 static void cleanup(void)
264 {
265 /* call cleanup function only once */
266 static int first_call = 1;
267 if (!first_call)
268 return;
269 first_call = 0;
270
271 set_user(NULL);
272
273 if (skip_cleanup)
274 return;
275
276 test_user_cmd("userdel -r");
277
278 if (disable_protected_hlinks) {
279 tst_resm(TINFO, "Disable protected hardlinks mode back");
280 switch_protected_hlinks(0);
281 }
282 if (disable_protected_slinks) {
283 tst_resm(TINFO, "Disable protected symlinks mode back");
284 switch_protected_slinks(0);
285 }
286
287 tst_rmdir();
288 }
289
get_protected_hlinks(void)290 static int get_protected_hlinks(void)
291 {
292 int value = 0;
293 SAFE_FILE_SCANF(cleanup, hrdlink_proc_path, "%d", &value);
294 return value;
295 }
296
get_protected_slinks(void)297 static int get_protected_slinks(void)
298 {
299 int value = 0;
300 SAFE_FILE_SCANF(cleanup, symlink_proc_path, "%d", &value);
301 return value;
302 }
303
switch_protected_hlinks(int value)304 static void switch_protected_hlinks(int value)
305 {
306 SAFE_FILE_PRINTF(cleanup, hrdlink_proc_path, "%d", value == 1);
307 }
308
switch_protected_slinks(int value)309 static void switch_protected_slinks(int value)
310 {
311 SAFE_FILE_PRINTF(cleanup, symlink_proc_path, "%d", value == 1);
312 }
313
test_user_cmd(const char * user_cmd)314 static void test_user_cmd(const char *user_cmd)
315 {
316 char cmd[MAX_CMD_LEN];
317 snprintf(cmd, MAX_CMD_LEN, "%s %s", user_cmd, users[TEST_USER].name);
318 if (system(cmd) != 0) {
319 tst_brkm(TBROK, cleanup, "Failed to run cmd: %s %s",
320 user_cmd, users[TEST_USER].name);
321 }
322 }
323
help(void)324 static void help(void)
325 {
326 printf(" -s Skip cleanup.\n");
327 printf(" -u #user name : Define test user\n");
328 }
329
create_sub_dir(const char * path,struct dir_params * bdir,mode_t mode)330 static void create_sub_dir(const char *path,
331 struct dir_params *bdir, mode_t mode)
332 {
333 snprintf(bdir->path, MAX_PATH, "%s/tmp_%s",
334 path, users[bdir->owner].name);
335 SAFE_MKDIR(cleanup, bdir->path, mode);
336
337 if (bdir->sticky)
338 mode |= S_ISVTX;
339 chmod(bdir->path, mode);
340 }
341
init_base_dirs(void)342 static void init_base_dirs(void)
343 {
344 char *cwd = tst_get_tmpdir();
345 cwd_offset = strlen(cwd);
346
347 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
348 chmod(cwd, mode);
349
350 strcpy(bdirs[0].path, cwd);
351 free(cwd);
352
353 bdirs[0].sticky = 0;
354 bdirs[0].world_writable = 1;
355
356 /* create subdir for each user */
357 int dir, usr;
358 for (usr = 0; usr < USERS_NUM; ++usr) {
359 set_user(users[usr].name);
360 dir = usr + 1;
361 bdirs[dir].sticky = 1;
362 bdirs[dir].world_writable = 1;
363 bdirs[dir].owner = usr;
364
365 create_sub_dir(bdirs[0].path, &bdirs[dir], mode);
366 }
367 }
368
init_files_dirs(void)369 static void init_files_dirs(void)
370 {
371 int dir, usr;
372 /* create all other dirs and files */
373 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
374 for (usr = 0; usr < USERS_NUM; ++usr) {
375 set_user(users[usr].name);
376 char path[MAX_PATH];
377
378 /* create file in the main directory */
379 snprintf(path, MAX_PATH, "%s/%s%s",
380 bdirs[dir].path, users[usr].name, file_ext);
381 ufiles_add(usr, path, IS_FILE);
382
383 /* create file with S_IWOTH bit set */
384 strcat(path, "_w");
385 ufiles_add(usr, path, IS_FILE);
386
387 chmod(path, S_IRUSR | S_IRGRP | S_IWOTH | S_IROTH);
388
389 /* create sub directory */
390 snprintf(path, MAX_PATH, "%s/%s", bdirs[dir].path,
391 users[usr].name);
392 ufiles_add(usr, path, IS_DIRECTORY);
393
394 /* create local file inside sub directory */
395 snprintf(path + strlen(path), MAX_PATH - strlen(path),
396 "/local_%s%s", users[usr].name, file_ext);
397 ufiles_add(usr, path, IS_FILE);
398 }
399 }
400 }
401
ufiles_add(int usr,char * path,int type)402 static void ufiles_add(int usr, char *path, int type)
403 {
404 int file = users[usr].num;
405
406 if (file >= MAX_ENTITIES)
407 tst_brkm(TBROK, cleanup, "Unexpected number of files");
408
409 struct user_file *ufile = &users[usr].file[file];
410
411 if (type == IS_FILE)
412 SAFE_TOUCH(cleanup, path, 0644, NULL);
413 else
414 SAFE_MKDIR(cleanup, path, 0755);
415
416 strcpy(ufile->path, path);
417
418 ufile->is_dir = (type == IS_DIRECTORY);
419 ++users[usr].num;
420 }
421
create_link_path(char * buffer,int size,const char * path)422 static void create_link_path(char *buffer, int size, const char *path)
423 {
424 /* to make sure name is unique */
425 static int count;
426 ++count;
427
428 /* construct link name */
429 snprintf(buffer, size, "%s/link_%d", path, count);
430 }
431
create_check_slinks(const struct user_file * ufile,int owner)432 static int create_check_slinks(const struct user_file *ufile, int owner)
433 {
434 int result = 0;
435 int dir, usr;
436 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
437 for (usr = 0; usr < USERS_NUM; ++usr) {
438 /* set user who will create symlink */
439 set_user(users[usr].name);
440
441 struct link_info slink_info;
442 create_link_path(slink_info.path, MAX_PATH,
443 bdirs[dir].path);
444
445 slink_info.owner = usr;
446 slink_info.source_owner = owner;
447 slink_info.in_world_write = bdirs[dir].world_writable;
448 slink_info.in_sticky = bdirs[dir].sticky;
449 slink_info.dir_owner = bdirs[dir].owner;
450
451 if (symlink(ufile->path, slink_info.path) == -1) {
452 tst_brkm(TBROK, cleanup,
453 "Can't create symlink: %s",
454 slink_info.path);
455 }
456 result |= check_symlink(&slink_info);
457 }
458 }
459 return result;
460 }
461
create_check_hlinks(const struct user_file * ufile,int owner)462 static int create_check_hlinks(const struct user_file *ufile, int owner)
463 {
464 int result = 0;
465 int dir, usr;
466 for (dir = 0; dir < ARRAY_SIZE(bdirs); ++dir) {
467 for (usr = 0; usr < USERS_NUM; ++usr) {
468 /* can't create hardlink to directory */
469 if (ufile->is_dir)
470 continue;
471 /* set user who will create hardlink */
472 set_user(users[usr].name);
473
474 struct link_info hlink_info;
475 create_link_path(hlink_info.path, MAX_PATH,
476 bdirs[dir].path);
477
478 int can_write = try_open(ufile->path, O_WRONLY) == 0;
479
480 int tst_flag = (can_write || usr == owner ||
481 usr == ROOT) ? CAN_CREATE : CANNOT_CREATE;
482
483 int fail;
484 fail = tst_flag != link(ufile->path, hlink_info.path);
485
486 result |= fail;
487 tst_resm((fail) ? TFAIL : TPASS,
488 "Expect: %s create hardlink '...%s' to '...%s', "
489 "owner '%s', curr.user '%s', w(%d)",
490 (tst_flag == CAN_CREATE) ? "can" : "can't",
491 ufile->path + cwd_offset,
492 hlink_info.path + cwd_offset,
493 users[owner].name, users[usr].name,
494 can_write);
495 }
496 }
497 return result;
498 }
499
check_symlink(const struct link_info * li)500 static int check_symlink(const struct link_info *li)
501 {
502 int symlink_result = 0;
503 int usr;
504 for (usr = 0; usr < USERS_NUM; ++usr) {
505 set_user(users[usr].name);
506 int tst_flag = (usr == li->dir_owner &&
507 li->in_world_write && li->in_sticky &&
508 usr != li->owner) ? CANNOT_FOLLOW : CAN_FOLLOW;
509
510 int fail = tst_flag != try_symlink(li->path);
511
512 symlink_result |= fail;
513
514 tst_resm((fail) ? TFAIL : TPASS,
515 "Expect: %s follow symlink '...%s', "
516 "owner '%s', src.owner '%s', "
517 "curr.user '%s', dir.owner '%s'",
518 (tst_flag == CAN_FOLLOW) ? "can" : "can't",
519 li->path + cwd_offset, users[li->owner].name,
520 users[li->source_owner].name, users[usr].name,
521 users[li->dir_owner].name);
522 }
523 return symlink_result;
524 }
525
526 /* differenet modes to try in the test */
527 static const int o_modes[] = {
528 O_RDONLY,
529 O_WRONLY,
530 O_RDWR,
531 O_RDONLY | O_NONBLOCK | O_DIRECTORY,
532 };
533
try_symlink(const char * name)534 static int try_symlink(const char *name)
535 {
536 int mode;
537 for (mode = 0; mode < ARRAY_SIZE(o_modes); ++mode) {
538 if (try_open(name, o_modes[mode]) != -1)
539 return CAN_FOLLOW;
540 }
541
542 return CANNOT_FOLLOW;
543 }
544
try_open(const char * name,int mode)545 static int try_open(const char *name, int mode)
546 {
547 int fd = open(name, mode);
548
549 if (fd == -1)
550 return fd;
551
552 if (close(fd) == -1)
553 tst_brkm(TBROK, cleanup, "Can't close file: %s", name);
554
555 return 0;
556 }
557
set_user(const char * name)558 static void set_user(const char *name)
559 {
560 uid_t user_id = 0;
561 gid_t user_gr = 0;
562
563 if (name != NULL) {
564 struct passwd *pswd = getpwnam(name);
565
566 if (pswd == 0) {
567 tst_brkm(TBROK, cleanup,
568 "Failed to find user '%s'", name);
569 }
570 user_id = pswd->pw_uid;
571 user_gr = pswd->pw_gid;
572 }
573
574 SAFE_SETEGID(cleanup, user_gr);
575 SAFE_SETEUID(cleanup, user_id);
576 }
577