1 /* $NetBSD: paxctl.c,v 1.2 2007/06/24 20:35:36 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.2 2007/06/24 20:35:36 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) __attribute__((__noreturn__)); 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(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(u_long f) 147 { 148 size_t i; 149 150 for (i = 0; i < __arraycount(flags); i++) 151 if (f & flags[i].bits) 152 (void)printf(" %c: %s\n", 153 flags[i].mark, flags[i].name); 154 } 155 156 int 157 main(int argc, char **argv) 158 { 159 union { 160 Elf32_Ehdr h32; 161 Elf64_Ehdr h64; 162 } e; 163 union { 164 Elf32_Phdr h32; 165 Elf64_Phdr h64; 166 } p; 167 union { 168 Elf32_Nhdr h32; 169 Elf64_Nhdr h64; 170 } n; 171 #define EH(field) (size == 32 ? e.h32.field : e.h64.field) 172 #define PH(field) (size == 32 ? p.h32.field : p.h64.field) 173 #define NH(field) (size == 32 ? n.h32.field : n.h64.field) 174 #define SPH(field, val) do { \ 175 if (size == 32) \ 176 p.h32.field val; \ 177 else \ 178 p.h64.field val; \ 179 } while (/*CONSTCOND*/0) 180 #define PHSIZE (size == 32 ? sizeof(p.h32) : sizeof(p.h64)) 181 #define NHSIZE (size == 32 ? sizeof(n.h32) : sizeof(n.h64)) 182 struct { 183 char name[ELF_NOTE_PAX_NAMESZ]; 184 uint32_t flags; 185 } pax_tag; 186 187 int size; 188 char *opt = NULL; 189 int fd, i, add_flags = 0, del_flags = 0, list = 0, ok = 0, flagged = 0; 190 191 if (argc < 2) 192 usage(); 193 194 for (i = 1; i < argc; i++) { 195 opt = argv[i]; 196 197 if (*opt == '-' || *opt == '+') { 198 int t; 199 200 t = pax_flag(opt + 1); 201 if (t == -1) 202 usage(); 203 204 if (*opt == '-') 205 del_flags |= t; 206 else 207 add_flags |= t; 208 209 opt = NULL; 210 } else 211 break; 212 } 213 214 if (opt == NULL) 215 usage(); 216 217 if (add_flags || del_flags) { 218 if (list) 219 usage(); 220 } else 221 list = 1; 222 223 fd = open(opt, O_RDWR, 0); 224 if (fd == -1) { 225 if (!list || (fd = open(opt, O_RDONLY, 0)) == -1) 226 err(EXIT_FAILURE, "Can't open `%s'", opt); 227 } 228 229 if (read(fd, &e, sizeof(e)) != sizeof(e)) 230 err(EXIT_FAILURE, "Can't read ELF header from `%s'", opt); 231 232 if (memcmp(e.h32.e_ident, ELFMAG, SELFMAG) != 0) 233 errx(EXIT_FAILURE, 234 "Bad ELF magic from `%s' (maybe it's not an ELF?)", opt); 235 236 if (e.h32.e_ehsize == sizeof(e.h32)) 237 size = 32; 238 else if (e.h64.e_ehsize == sizeof(e.h64)) 239 size = 64; 240 else 241 errx(EXIT_FAILURE, 242 "Bad ELF size %d from `%s' (maybe it's not an ELF?)", 243 (int)e.h32.e_ehsize, opt); 244 245 for (i = 0; i < EH(e_phnum); i++) { 246 if (pread(fd, &p, PHSIZE, 247 (off_t)EH(e_phoff) + i * PHSIZE) != PHSIZE) 248 err(EXIT_FAILURE, "Can't read program header data" 249 " from `%s'", opt); 250 251 if (PH(p_type) != PT_NOTE) 252 continue; 253 254 if (pread(fd, &n, NHSIZE, (off_t)PH(p_offset)) != NHSIZE) 255 err(EXIT_FAILURE, "Can't read note header from `%s'", 256 opt); 257 if (NH(n_type) != ELF_NOTE_TYPE_PAX_TAG || 258 NH(n_descsz) != ELF_NOTE_PAX_DESCSZ || 259 NH(n_namesz) != ELF_NOTE_PAX_NAMESZ) 260 continue; 261 if (pread(fd, &pax_tag, sizeof(pax_tag), PH(p_offset) + NHSIZE) 262 != sizeof(pax_tag)) 263 err(EXIT_FAILURE, "Can't read pax_tag from `%s'", 264 opt); 265 if (memcmp(pax_tag.name, ELF_NOTE_PAX_NAME, 266 sizeof(pax_tag.name)) != 0) 267 err(EXIT_FAILURE, "Unknown pax_tag name `%*.*s' from" 268 " `%s'", ELF_NOTE_PAX_NAMESZ, ELF_NOTE_PAX_NAMESZ, 269 pax_tag.name, opt); 270 ok = 1; 271 272 if (list) { 273 if (!pax_haveflags(pax_tag.flags)) 274 break; 275 276 if (!pax_flags_sane(pax_tag.flags)) 277 warnx("Current flags %x don't make sense", 278 pax_tag.flags); 279 280 (void)printf("PaX flags:\n"); 281 282 pax_printflags(pax_tag.flags); 283 284 flagged = 1; 285 286 break; 287 } 288 289 pax_tag.flags |= add_flags; 290 pax_tag.flags &= ~del_flags; 291 292 if (!pax_flags_sane(pax_tag.flags)) 293 errx(EXIT_FAILURE, "New flags %x don't make sense", 294 pax_tag.flags); 295 296 if (pwrite(fd, &pax_tag, sizeof(pax_tag), 297 (off_t)PH(p_offset) + NHSIZE) != sizeof(pax_tag)) 298 err(EXIT_FAILURE, "Can't modify flags on `%s'", opt); 299 break; 300 } 301 302 (void)close(fd); 303 304 if (!ok) 305 errx(EXIT_FAILURE, 306 "Could not find an ELF PaX PT_NOTE section in `%s'", opt); 307 308 if (list && !flagged) 309 (void)printf("No PaX flags.\n"); 310 return 0; 311 } 312