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