1 /*- 2 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #if __FreeBSD_version > 1001510 32 #include <sys/capsicum.h> 33 #else 34 #include <sys/capability.h> 35 #endif 36 #include <sys/types.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <stringlist.h> 47 #include <termios.h> 48 #include <unistd.h> 49 50 #define C_OPTION 0x1 51 52 static StringList *includes; 53 54 static void 55 usage(void) 56 { 57 58 fprintf(stderr, "usage: soelim [-Crtv] [-I dir] [files]\n"); 59 60 exit(EXIT_FAILURE); 61 } 62 63 static const char * 64 relpath(const char *path) 65 { 66 67 while (*path == '/' && *path != '\0') 68 path++; 69 70 return (path); 71 } 72 73 static FILE * 74 soelim_fopen(int rootfd, const char *name) 75 { 76 FILE *f = NULL; 77 char path[PATH_MAX]; 78 size_t i; 79 int fd; 80 81 if (strcmp(name, "-") == 0) 82 return (stdin); 83 84 if ((fd = openat(rootfd, relpath(name), O_RDONLY)) != -1) { 85 f = fdopen(fd, "r"); 86 goto out; 87 } 88 89 if (*name == '/') { 90 warn("can't open '%s'", name); 91 return (NULL); 92 } 93 94 for (i = 0; i < includes->sl_cur; i++) { 95 snprintf(path, sizeof(path), "%s/%s", includes->sl_str[i], 96 name); 97 if ((fd = openat(rootfd, relpath(path), O_RDONLY)) != -1) { 98 f = fdopen(fd, "r"); 99 break; 100 } 101 } 102 103 out: 104 if (f == NULL) 105 warn("can't open '%s'", name); 106 107 return (f); 108 } 109 110 static int 111 soelim_file(int rootfd, FILE *f, int flag) 112 { 113 char *line = NULL; 114 char *walk, *cp; 115 size_t linecap = 0; 116 ssize_t linelen; 117 118 if (f == NULL) 119 return (1); 120 121 while ((linelen = getline(&line, &linecap, f)) > 0) { 122 if (strncmp(line, ".so", 3) != 0) { 123 printf("%s", line); 124 continue; 125 } 126 127 walk = line + 3; 128 if (!isspace(*walk) && ((flag & C_OPTION) == 0)) { 129 printf("%s", line); 130 continue; 131 } 132 133 while (isspace(*walk)) 134 walk++; 135 136 cp = walk; 137 while (*cp != '\0' && !isspace(*cp)) 138 cp++; 139 *cp = 0; 140 if (cp < line + linelen) 141 cp++; 142 143 if (*walk == '\0') { 144 printf("%s", line); 145 continue; 146 } 147 if (soelim_file(rootfd, soelim_fopen(rootfd, walk), flag) == 1) { 148 free(line); 149 return (1); 150 } 151 if (*cp != '\0') 152 printf("%s", cp); 153 } 154 155 free(line); 156 fclose(f); 157 158 return (0); 159 } 160 161 int 162 main(int argc, char **argv) 163 { 164 int ch, i, rootfd; 165 int ret = 0; 166 int flags = 0; 167 char cwd[MAXPATHLEN]; 168 unsigned long cmd; 169 cap_rights_t rights; 170 171 includes = sl_init(); 172 if (getcwd(cwd, sizeof(cwd)) != NULL) 173 sl_add(includes, cwd); 174 175 if (includes == NULL) 176 err(EXIT_FAILURE, "sl_init()"); 177 178 while ((ch = getopt(argc, argv, "CrtvI:")) != -1) { 179 switch (ch) { 180 case 'C': 181 flags |= C_OPTION; 182 break; 183 case 'r': 184 case 'v': 185 case 't': 186 /* stub compatibility with groff's soelim */ 187 break; 188 case 'I': 189 sl_add(includes, optarg); 190 break; 191 default: 192 sl_free(includes, 0); 193 usage(); 194 } 195 } 196 197 argc -= optind; 198 argv += optind; 199 200 rootfd = open("/", O_DIRECTORY | O_RDONLY); 201 if (rootfd == -1) 202 err(EXIT_FAILURE, "unable to open '/'"); 203 cap_rights_init(&rights, CAP_READ, CAP_FSTAT, CAP_IOCTL); 204 /* 205 * EBADF in case stdin is closed by the caller 206 */ 207 if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS 208 && errno != EBADF) 209 err(EXIT_FAILURE, "unable to limit rights for stdin"); 210 cap_rights_init(&rights, CAP_WRITE, CAP_FSTAT, CAP_IOCTL); 211 if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) 212 err(EXIT_FAILURE, "unable to limit rights for stdout"); 213 if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) 214 err(EXIT_FAILURE, "unable to limit rights for stderr"); 215 cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_FSTAT, CAP_FCNTL); 216 if (cap_rights_limit(rootfd, &rights) < 0 && errno != ENOSYS) 217 err(EXIT_FAILURE, "unable to limit rights"); 218 219 cmd = TIOCGETA; 220 if (cap_ioctls_limit(STDOUT_FILENO, &cmd, 1) < 0 && errno != ENOSYS) 221 err(EXIT_FAILURE, "unable to limit ioctls for stdout"); 222 if (cap_ioctls_limit(STDERR_FILENO, &cmd, 1) < 0 && errno != ENOSYS) 223 err(EXIT_FAILURE, "unable to limit ioctls for stderr"); 224 if (cap_ioctls_limit(STDIN_FILENO, &cmd, 1) < 0 && errno != ENOSYS) 225 err(EXIT_FAILURE, "unable to limit ioctls for stdin"); 226 227 if (cap_enter() < 0 && errno != ENOSYS) 228 err(EXIT_FAILURE, "unable to enter capability mode"); 229 230 if (argc == 0) 231 ret = soelim_file(rootfd, stdin, flags); 232 233 for (i = 0; i < argc; i++) 234 ret = soelim_file(rootfd, soelim_fopen(rootfd, argv[i]), flags); 235 236 sl_free(includes, 0); 237 close(rootfd); 238 239 return (ret); 240 } 241