1 /* 2 * Check decoding of chown/chown32/lchown/lchown32/fchown/fchown32 syscalls. 3 * 4 * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <fcntl.h> 31 #include <stdio.h> 32 #include <unistd.h> 33 34 #ifdef UGID_TYPE_IS_SHORT 35 # define UGID_TYPE short 36 # define GETEUID syscall(__NR_geteuid) 37 # define GETEGID syscall(__NR_getegid) 38 # define CHECK_OVERFLOWUID(arg) check_overflowuid(arg) 39 # define CHECK_OVERFLOWGID(arg) check_overflowgid(arg) 40 #else 41 # define UGID_TYPE int 42 # define GETEUID geteuid() 43 # define GETEGID getegid() 44 # define CHECK_OVERFLOWUID(arg) 45 # define CHECK_OVERFLOWGID(arg) 46 #endif 47 48 #define UNLINK_SAMPLE \ 49 if (unlink(sample)) perror_msg_and_fail("unlink") 50 #define CLOSE_SAMPLE \ 51 if (close(fd)) perror_msg_and_fail("close") 52 53 #ifdef ACCESS_BY_DESCRIPTOR 54 # define SYSCALL_ARG1 fd 55 # define FMT_ARG1 "%d" 56 # define EOK_CMD CLOSE_SAMPLE 57 # define CLEANUP_CMD UNLINK_SAMPLE 58 #else 59 # define SYSCALL_ARG1 sample 60 # define FMT_ARG1 "\"%s\"" 61 # define EOK_CMD UNLINK_SAMPLE 62 # define CLEANUP_CMD CLOSE_SAMPLE 63 #endif 64 65 static int 66 ugid2int(const unsigned UGID_TYPE id) 67 { 68 if ((unsigned UGID_TYPE) -1U == id) 69 return -1; 70 else 71 return id; 72 } 73 74 static void 75 print_int(const unsigned int num) 76 { 77 if (num == -1U) 78 printf(", -1"); 79 else 80 printf(", %u", num); 81 } 82 83 static int 84 num_matches_id(const unsigned int num, const unsigned int id) 85 { 86 return num == id || num == -1U; 87 } 88 89 #define PAIR(val) { val, gid }, { uid, val } 90 91 int 92 main(void) 93 { 94 static const char sample[] = SYSCALL_NAME "_sample"; 95 96 unsigned int uid = GETEUID; 97 CHECK_OVERFLOWUID(uid); 98 unsigned int gid = GETEGID; 99 CHECK_OVERFLOWUID(gid); 100 101 const struct { 102 const long uid, gid; 103 } tests[] = { 104 { uid, gid }, 105 { (unsigned long) 0xffffffff00000000ULL | uid, gid }, 106 { uid, (unsigned long) 0xffffffff00000000ULL | gid }, 107 PAIR(-1U), 108 PAIR(-1L), 109 { 0xffff0000U | uid, gid }, 110 { uid, 0xffff0000U | gid }, 111 PAIR(0xffff), 112 PAIR(0xc0deffffU), 113 PAIR(0xfacefeedU), 114 PAIR((long) 0xfacefeeddeadbeefULL) 115 }; 116 117 int fd = open(sample, O_RDONLY | O_CREAT, 0400); 118 if (fd < 0) 119 perror_msg_and_fail("open"); 120 121 CLEANUP_CMD; 122 123 unsigned int i; 124 long expected = 0; 125 126 for (i = 0; i < ARRAY_SIZE(tests); ++i) { 127 const unsigned int unum = ugid2int(tests[i].uid); 128 const unsigned int gnum = ugid2int(tests[i].gid); 129 130 if (num_matches_id(unum, uid) && 131 num_matches_id(gnum, gid)) { 132 if (expected) 133 continue; 134 } else { 135 if (!expected) { 136 expected = -1; 137 EOK_CMD; 138 } 139 } 140 141 const long rc = syscall(SYSCALL_NR, SYSCALL_ARG1, 142 tests[i].uid, tests[i].gid); 143 const char *errstr = sprintrc(rc); 144 printf("%s(" FMT_ARG1, SYSCALL_NAME, SYSCALL_ARG1); 145 print_int(unum); 146 print_int(gnum); 147 printf(") = %s\n", errstr); 148 } 149 150 puts("+++ exited with 0 +++"); 151 return 0; 152 } 153