1 /* $NetBSD: paxctl.c,v 1.5 2007/12/15 19:44:56 perry Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Elad Efrat <elad@NetBSD.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 #if HAVE_NBTOOL_CONFIG_H 30 #include "nbtool_config.h" 31 #endif 32 33 #include <sys/cdefs.h> 34 #ifndef lint 35 #ifdef __RCSID 36 __RCSID("$NetBSD: paxctl.c,v 1.5 2007/12/15 19:44:56 perry Exp $"); 37 #endif 38 #endif /* not lint */ 39 40 #include <sys/types.h> 41 #ifdef HAVE_NBTOOL_CONFIG_H 42 #include "../../sys/sys/exec_elf.h" 43 #else 44 #include <elf.h> 45 #endif 46 #include <stdio.h> 47 #include <err.h> 48 #include <fcntl.h> 49 #include <unistd.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 static void usage(void) __dead; 54 static int pax_flag(const char *); 55 static int pax_flags_sane(u_long); 56 static int pax_haveflags(u_long); 57 static void pax_printflags(const char *, int, u_long); 58 59 #ifndef ELF_NOTE_TYPE_PAX_TAG 60 /* NetBSD-specific note type: PaX. There should be 1 NOTE per executable. 61 section. desc is a 32 bit bitmask */ 62 #define ELF_NOTE_TYPE_PAX_TAG 3 63 #define ELF_NOTE_PAX_MPROTECT 0x1 /* Force enable Mprotect */ 64 #define ELF_NOTE_PAX_NOMPROTECT 0x2 /* Force disable Mprotect */ 65 #define ELF_NOTE_PAX_GUARD 0x4 /* Force enable Segvguard */ 66 #define ELF_NOTE_PAX_NOGUARD 0x8 /* Force disable Servguard */ 67 #define ELF_NOTE_PAX_NAMESZ 4 68 #define ELF_NOTE_PAX_NAME "PaX\0" 69 #define ELF_NOTE_PAX_DESCSZ 4 70 #endif 71 #ifndef __arraycount 72 #define __arraycount(a) (sizeof(a) / sizeof(a[0])) 73 #endif 74 75 76 static const struct paxflag { 77 char mark; 78 const char *name; 79 int bits; 80 } flags[] = { 81 { 'G', "Segvguard, explicit enable", 82 ELF_NOTE_PAX_GUARD }, 83 { 'g', "Segvguard, explicit disable", 84 ELF_NOTE_PAX_NOGUARD }, 85 { 'M', "mprotect(2) restrictions, explicit enable", 86 ELF_NOTE_PAX_MPROTECT }, 87 { 'm', "mprotect(2) restrictions, explicit disable", 88 ELF_NOTE_PAX_NOMPROTECT }, 89 }; 90 91 static void 92 usage(void) 93 { 94 (void)fprintf(stderr, "Usage: %s [ <-|+><G|g|M|m> ] <file> ...\n", 95 #if HAVE_NBTOOL_CONFIG_H 96 "paxctl" 97 #else 98 getprogname() 99 #endif 100 ); 101 exit(1); 102 } 103 104 static int 105 pax_flag(const char *s) 106 { 107 size_t i; 108 109 if (s[0] == '\0' || s[1] != '\0') 110 return -1; 111 112 for (i = 0; i < __arraycount(flags); i++) 113 if (*s == flags[i].mark) 114 return flags[i].bits; 115 116 return -1; 117 } 118 119 static int 120 pax_flags_sane(u_long f) 121 { 122 size_t i; 123 124 for (i = 0; i < __arraycount(flags) - 1; i += 2) { 125 int g = flags[i].bits | flags[i+1].bits; 126 if ((f & g) == g) 127 return 0; 128 } 129 130 return 1; 131 } 132 133 static int 134 pax_haveflags(u_long f) 135 { 136 size_t i; 137 138 for (i = 0; i < __arraycount(flags); i++) 139 if (f & flags[i].bits) 140 return (1); 141 142 return (0); 143 } 144 145 static void 146 pax_printflags(const char *name, int many, u_long f) 147 { 148 size_t i; 149 150 for (i = 0; i < __arraycount(flags); i++) 151 if (f & flags[i].bits) { 152 if (many) 153 (void)printf("%s: ", name); 154 (void)printf(" %c: %s\n", 155 flags[i].mark, flags[i].name); 156 } 157 } 158 159 static int 160 process_one(const char *name, int add_flags, int del_flags, int list, int many) 161 { 162 union { 163 Elf32_Ehdr h32; 164 Elf64_Ehdr h64; 165 } e; 166 union { 167 Elf32_Phdr h32; 168 Elf64_Phdr h64; 169 } p; 170 union { 171 Elf32_Nhdr h32; 172 Elf64_Nhdr h64; 173 } n; 174 #define EH(field) (size == 32 ? e.h32.field : e.h64.field) 175 #define PH(field) (size == 32 ? p.h32.field : p.h64.field) 176 #define NH(field) (size == 32 ? n.h32.field : n.h64.field) 177 #define SPH(field, val) do { \ 178 if (size == 32) \ 179 p.h32.field val; \ 180 else \ 181 p.h64.field val; \ 182 } while (/*CONSTCOND*/0) 183 #define PHSIZE (size == 32 ? sizeof(p.h32) : sizeof(p.h64)) 184 #define NHSIZE (size == 32 ? sizeof(n.h32) : sizeof(n.h64)) 185 struct { 186 char name[ELF_NOTE_PAX_NAMESZ]; 187 uint32_t flags; 188 } pax_tag; 189 int i, fd, size, ok = 0, flagged = 0; 190 191 fd = open(name, list ? O_RDONLY: O_RDWR, 0); 192 if (fd == -1) { 193 warn("Can't open `%s'", name); 194 return 1; 195 } 196 197 if (read(fd, &e, sizeof(e)) != sizeof(e)) { 198 warn("Can't read ELF header from `%s'", name); 199 return 1; 200 } 201 202 if (memcmp(e.h32.e_ident, ELFMAG, SELFMAG) != 0) { 203 warn("Bad ELF magic from `%s' (maybe it's not an ELF?)", name); 204 return 1; 205 } 206 207 if (e.h32.e_ehsize == sizeof(e.h32)) 208 size = 32; 209 else if (e.h64.e_ehsize == sizeof(e.h64)) 210 size = 64; 211 else { 212 warn("Bad ELF size %d from `%s' (maybe it's not an ELF?)", 213 (int)e.h32.e_ehsize, name); 214 return 1; 215 } 216 217 for (i = 0; i < EH(e_phnum); i++) { 218 if (pread(fd, &p, PHSIZE, (off_t)EH(e_phoff) + i * PHSIZE) != 219 PHSIZE) { 220 warn("Can't read program header data from `%s'", name); 221 return 1; 222 } 223 224 if (PH(p_type) != PT_NOTE) 225 continue; 226 227 if (pread(fd, &n, NHSIZE, (off_t)PH(p_offset)) != NHSIZE) { 228 warn("Can't read note header from `%s'", name); 229 return 1; 230 } 231 if (NH(n_type) != ELF_NOTE_TYPE_PAX_TAG || 232 NH(n_descsz) != ELF_NOTE_PAX_DESCSZ || 233 NH(n_namesz) != ELF_NOTE_PAX_NAMESZ) 234 continue; 235 if (pread(fd, &pax_tag, sizeof(pax_tag), PH(p_offset) + NHSIZE) 236 != sizeof(pax_tag)) { 237 warn("Can't read pax_tag from `%s'", name); 238 return 1; 239 } 240 if (memcmp(pax_tag.name, ELF_NOTE_PAX_NAME, 241 sizeof(pax_tag.name)) != 0) { 242 warn("Unknown pax_tag name `%*.*s' from `%s'", 243 ELF_NOTE_PAX_NAMESZ, ELF_NOTE_PAX_NAMESZ, 244 pax_tag.name, name); 245 return 1; 246 } 247 ok = 1; 248 249 if (list) { 250 if (!pax_haveflags(pax_tag.flags)) 251 break; 252 253 if (!pax_flags_sane(pax_tag.flags)) 254 warnx("Current flags %x don't make sense", 255 pax_tag.flags); 256 257 if (many) 258 (void)printf("%s: ", name); 259 (void)printf("PaX flags:\n"); 260 261 pax_printflags(name, many, pax_tag.flags); 262 263 flagged = 1; 264 265 break; 266 } 267 268 pax_tag.flags |= add_flags; 269 pax_tag.flags &= ~del_flags; 270 271 if (!pax_flags_sane(pax_tag.flags)) { 272 warn("New flags %x don't make sense", pax_tag.flags); 273 return 1; 274 } 275 276 if (pwrite(fd, &pax_tag, sizeof(pax_tag), 277 (off_t)PH(p_offset) + NHSIZE) != sizeof(pax_tag)) 278 warn("Can't modify flags on `%s'", name); 279 break; 280 } 281 282 (void)close(fd); 283 284 if (!ok) { 285 warn("Could not find an ELF PaX PT_NOTE section in `%s'", name); 286 return 1; 287 } 288 289 if (list && !flagged) { 290 if (many) 291 (void)printf("%s: ", name); 292 (void)printf("No PaX flags.\n"); 293 } 294 return 0; 295 } 296 297 int 298 main(int argc, char **argv) 299 { 300 char *opt; 301 int i, add_flags = 0, del_flags = 0, list = 0, bad = 0, many; 302 303 if (argc < 2) 304 usage(); 305 306 for (i = 1; i < argc; i++) { 307 opt = argv[i]; 308 309 if (*opt == '-' || *opt == '+') { 310 int t; 311 312 t = pax_flag(opt + 1); 313 if (t == -1) 314 usage(); 315 316 if (*opt == '-') 317 del_flags |= t; 318 else 319 add_flags |= t; 320 321 opt = NULL; 322 } else 323 break; 324 } 325 326 if (i == argc) 327 usage(); 328 329 if (add_flags || del_flags) { 330 if (list) 331 usage(); 332 } else 333 list = 1; 334 335 many = i != argc - 1; 336 for (; i < argc; i++) 337 bad |= process_one(argv[i], add_flags, del_flags, list, many); 338 339 return bad ? EXIT_FAILURE : 0; 340 } 341