1 /*- 2 * Copyright (c) 1983, 1993 3 * The Regents of the University of California. 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 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * @(#) Copyright (c) 1983, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)uudecode.c 8.2 (Berkeley) 4/2/94 31 * $FreeBSD: src/usr.bin/uudecode/uudecode.c,v 1.13.2.6 2003/04/07 20:10:33 fanf Exp $ 32 */ 33 34 /* 35 * uudecode [file ...] 36 * 37 * create the specified file, decoding as you go. 38 * used with uuencode. 39 */ 40 #include <sys/param.h> 41 #include <sys/socket.h> 42 #include <sys/stat.h> 43 44 #include <netinet/in.h> 45 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <libgen.h> 50 #include <pwd.h> 51 #include <resolv.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 #ifdef BOOTSTRAPPING 58 #define getline get_line /* help bootstrap previous stdio.h */ 59 #endif 60 61 static const char *infile, *outfile; 62 static FILE *infp, *outfp; 63 static int base64, cflag, iflag, oflag, pflag, rflag, sflag; 64 65 static void usage(void); 66 static int decode(void); 67 static int decode2(void); 68 static int uu_decode(void); 69 static int base64_decode(void); 70 71 int 72 main(int argc, char *argv[]) 73 { 74 int rval, ch; 75 76 if (strcmp(basename(argv[0]), "b64decode") == 0) 77 base64 = 1; 78 79 while ((ch = getopt(argc, argv, "cimo:prs")) != -1) { 80 switch (ch) { 81 case 'c': 82 if (oflag || rflag) 83 usage(); 84 cflag = 1; /* multiple uudecode'd files */ 85 break; 86 case 'i': 87 iflag = 1; /* ask before override files */ 88 break; 89 case 'm': 90 base64 = 1; 91 break; 92 case 'o': 93 if (cflag || pflag || rflag || sflag) 94 usage(); 95 oflag = 1; /* output to the specified file */ 96 sflag = 1; /* do not strip pathnames for output */ 97 outfile = optarg; /* set the output filename */ 98 break; 99 case 'p': 100 if (oflag) 101 usage(); 102 pflag = 1; /* print output to stdout */ 103 break; 104 case 'r': 105 if (cflag || oflag) 106 usage(); 107 rflag = 1; /* decode raw data */ 108 break; 109 case 's': 110 if (oflag) 111 usage(); 112 sflag = 1; /* do not strip pathnames for output */ 113 break; 114 default: 115 usage(); 116 } 117 } 118 argc -= optind; 119 argv += optind; 120 121 if (*argv != NULL) { 122 rval = 0; 123 do { 124 infp = fopen(infile = *argv, "r"); 125 if (infp == NULL) { 126 warn("%s", *argv); 127 rval = 1; 128 continue; 129 } 130 rval |= decode(); 131 fclose(infp); 132 } while (*++argv); 133 } else { 134 infile = "stdin"; 135 infp = stdin; 136 rval = decode(); 137 } 138 exit(rval); 139 } 140 141 static int 142 decode(void) 143 { 144 int r, v; 145 146 if (rflag) { 147 /* relaxed alternative to decode2() */ 148 outfile = "/dev/stdout"; 149 outfp = stdout; 150 if (base64) 151 return (base64_decode()); 152 else 153 return (uu_decode()); 154 } 155 v = decode2(); 156 if (v == EOF) { 157 warnx("%s: missing or bad \"begin\" line", infile); 158 return (1); 159 } 160 for (r = v; cflag; r |= v) { 161 v = decode2(); 162 if (v == EOF) 163 break; 164 } 165 return (r); 166 } 167 168 static int 169 decode2(void) 170 { 171 int flags, fd; 172 size_t n, m; 173 char *p, *q; 174 void *handle; 175 mode_t mode; 176 struct passwd *pw; 177 struct stat st; 178 char buf[MAXPATHLEN + 1]; 179 180 base64 = 0; 181 /* search for header line */ 182 for (;;) { 183 if (fgets(buf, sizeof(buf), infp) == NULL) 184 return (EOF); 185 p = buf; 186 if (strncmp(p, "begin-base64 ", 13) == 0) { 187 base64 = 1; 188 p += 13; 189 } else if (strncmp(p, "begin ", 6) == 0) 190 p += 6; 191 else 192 continue; 193 /* p points to mode */ 194 q = strchr(p, ' '); 195 if (q == NULL) 196 continue; 197 *q++ = '\0'; 198 /* q points to filename */ 199 n = strlen(q); 200 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 201 q[--n] = '\0'; 202 /* found valid header? */ 203 if (n > 0) 204 break; 205 } 206 207 errno = 0; 208 if ((handle = setmode(p)) == NULL) { 209 if (!errno) 210 warnx("invalid file mode: %s", infile); 211 else 212 warn("setmode malloc failed: %s", infile); 213 214 return (1); 215 } 216 217 mode = getmode(handle, 0) & 0666; 218 free(handle); 219 220 if (sflag) { 221 /* don't strip, so try ~user/file expansion */ 222 p = NULL; 223 pw = NULL; 224 if (*q == '~') 225 p = strchr(q, '/'); 226 if (p != NULL) { 227 *p = '\0'; 228 pw = getpwnam(q + 1); 229 *p = '/'; 230 } 231 if (pw != NULL) { 232 n = strlen(pw->pw_dir); 233 if (buf + n > p) { 234 /* make room */ 235 m = strlen(p); 236 if (sizeof(buf) < n + m) { 237 warnx("%s: bad output filename", 238 infile); 239 return (1); 240 } 241 p = memmove(buf + n, p, m); 242 } 243 q = memcpy(p - n, pw->pw_dir, n); 244 } 245 } else { 246 /* strip down to leaf name */ 247 p = strrchr(q, '/'); 248 if (p != NULL) 249 q = p + 1; 250 } 251 if (!oflag) 252 outfile = q; 253 254 /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ 255 if (pflag || strcmp(outfile, "/dev/stdout") == 0) 256 outfp = stdout; 257 else { 258 flags = O_WRONLY | O_CREAT | O_EXCL; 259 if (lstat(outfile, &st) == 0) { 260 if (iflag) { 261 warnc(EEXIST, "%s: %s", infile, outfile); 262 return (0); 263 } 264 switch (st.st_mode & S_IFMT) { 265 case S_IFREG: 266 case S_IFLNK: 267 /* avoid symlink attacks */ 268 if (unlink(outfile) == 0 || errno == ENOENT) 269 break; 270 warn("%s: unlink %s", infile, outfile); 271 return (1); 272 case S_IFDIR: 273 warnc(EISDIR, "%s: %s", infile, outfile); 274 return (1); 275 default: 276 if (oflag) { 277 /* trust command-line names */ 278 flags &= ~O_EXCL; 279 break; 280 } 281 warnc(EEXIST, "%s: %s", infile, outfile); 282 return (1); 283 } 284 } else if (errno != ENOENT) { 285 warn("%s: %s", infile, outfile); 286 return (1); 287 } 288 if ((fd = open(outfile, flags, mode)) < 0 || 289 (outfp = fdopen(fd, "w")) == NULL) { 290 warn("%s: %s", infile, outfile); 291 return (1); 292 } 293 } 294 295 if (base64) 296 return (base64_decode()); 297 else 298 return (uu_decode()); 299 } 300 301 static int 302 get_line(char *buf, size_t size) 303 { 304 if (fgets(buf, size, infp) != NULL) 305 return (2); 306 if (rflag) 307 return (0); 308 warnx("%s: %s: short file", infile, outfile); 309 return (1); 310 } 311 312 static int 313 checkend(const char *ptr, const char *end, const char *msg) 314 { 315 size_t n; 316 317 n = strlen(end); 318 if (strncmp(ptr, end, n) != 0 || 319 strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { 320 warnx("%s: %s: %s", infile, outfile, msg); 321 return (1); 322 } 323 if (fclose(outfp) != 0) { 324 warn("%s: %s", infile, outfile); 325 return (1); 326 } 327 return (0); 328 } 329 330 static int 331 uu_decode(void) 332 { 333 int i, ch; 334 char *p; 335 char buf[MAXPATHLEN+1]; 336 337 /* for each input line */ 338 for (;;) { 339 switch (get_line(buf, sizeof(buf))) { 340 case 0: 341 return (0); 342 case 1: 343 return (1); 344 } 345 346 #define DEC(c) (((c) - ' ') & 077) /* single character decode */ 347 #define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 348 349 #define OUT_OF_RANGE do { \ 350 warnx("%s: %s: character out of range: [%d-%d]", \ 351 infile, outfile, 1 + ' ', 077 + ' ' + 1); \ 352 return (1); \ 353 } while (0) 354 355 /* 356 * `i' is used to avoid writing out all the characters 357 * at the end of the file. 358 */ 359 p = buf; 360 if ((i = DEC(*p)) <= 0) 361 break; 362 for (++p; i > 0; p += 4, i -= 3) 363 if (i >= 3) { 364 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 365 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 366 OUT_OF_RANGE; 367 368 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 369 putc(ch, outfp); 370 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 371 putc(ch, outfp); 372 ch = DEC(p[2]) << 6 | DEC(p[3]); 373 putc(ch, outfp); 374 } else { 375 if (i >= 1) { 376 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 377 OUT_OF_RANGE; 378 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 379 putc(ch, outfp); 380 } 381 if (i >= 2) { 382 if (!(IS_DEC(*(p + 1)) && 383 IS_DEC(*(p + 2)))) 384 OUT_OF_RANGE; 385 386 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 387 putc(ch, outfp); 388 } 389 if (i >= 3) { 390 if (!(IS_DEC(*(p + 2)) && 391 IS_DEC(*(p + 3)))) 392 OUT_OF_RANGE; 393 ch = DEC(p[2]) << 6 | DEC(p[3]); 394 putc(ch, outfp); 395 } 396 } 397 } 398 switch (get_line(buf, sizeof(buf))) { 399 case 0: 400 return (0); 401 case 1: 402 return (1); 403 default: 404 return (checkend(buf, "end", "no \"end\" line")); 405 } 406 } 407 408 static int 409 base64_decode(void) 410 { 411 int n; 412 char inbuf[MAXPATHLEN + 1]; 413 unsigned char outbuf[MAXPATHLEN * 4]; 414 415 for (;;) { 416 switch (get_line(inbuf, sizeof(inbuf))) { 417 case 0: 418 return (0); 419 case 1: 420 return (1); 421 } 422 423 n = b64_pton(inbuf, outbuf, sizeof(outbuf)); 424 425 if (n < 0) 426 break; 427 fwrite(outbuf, 1, n, outfp); 428 } 429 return (checkend(inbuf, "====", 430 "error decoding base64 input stream")); 431 } 432 433 static void 434 usage(void) 435 { 436 (void)fprintf(stderr, 437 "usage: uudecode [-cimprs] [file ...]\n" 438 " uudecode [-i] -o output_file [file]\n" 439 " b64decode [-cimprs] [file ...]\n" 440 " b64decode [-i] -o output_file [file]\n"); 441 exit(1); 442 } 443