uudecode.c revision 216370
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 * 4. 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 30#if 0 31#ifndef lint 32static const char copyright[] = 33"@(#) Copyright (c) 1983, 1993\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35#endif /* not lint */ 36 37#ifndef lint 38static char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94"; 39#endif /* not lint */ 40#endif 41#include <sys/cdefs.h> 42__FBSDID("$FreeBSD: head/usr.bin/uudecode/uudecode.c 216370 2010-12-11 08:32:16Z joel $"); 43 44/* 45 * uudecode [file ...] 46 * 47 * create the specified file, decoding as you go. 48 * used with uuencode. 49 */ 50#include <sys/param.h> 51#include <sys/socket.h> 52#include <sys/stat.h> 53 54#include <netinet/in.h> 55 56#include <ctype.h> 57#include <err.h> 58#include <errno.h> 59#include <fcntl.h> 60#include <libgen.h> 61#include <pwd.h> 62#include <resolv.h> 63#include <stdio.h> 64#include <stdlib.h> 65#include <string.h> 66#include <unistd.h> 67 68static const char *infile, *outfile; 69static FILE *infp, *outfp; 70static int base64, cflag, iflag, oflag, pflag, rflag, sflag; 71 72static void usage(void); 73static int decode(void); 74static int decode2(void); 75static int uu_decode(void); 76static int base64_decode(void); 77 78int 79main(int argc, char *argv[]) 80{ 81 int rval, ch; 82 83 if (strcmp(basename(argv[0]), "b64decode") == 0) 84 base64 = 1; 85 86 while ((ch = getopt(argc, argv, "cimo:prs")) != -1) { 87 switch (ch) { 88 case 'c': 89 if (oflag || rflag) 90 usage(); 91 cflag = 1; /* multiple uudecode'd files */ 92 break; 93 case 'i': 94 iflag = 1; /* ask before override files */ 95 break; 96 case 'm': 97 base64 = 1; 98 break; 99 case 'o': 100 if (cflag || pflag || rflag || sflag) 101 usage(); 102 oflag = 1; /* output to the specified file */ 103 sflag = 1; /* do not strip pathnames for output */ 104 outfile = optarg; /* set the output filename */ 105 break; 106 case 'p': 107 if (oflag) 108 usage(); 109 pflag = 1; /* print output to stdout */ 110 break; 111 case 'r': 112 if (cflag || oflag) 113 usage(); 114 rflag = 1; /* decode raw data */ 115 break; 116 case 's': 117 if (oflag) 118 usage(); 119 sflag = 1; /* do not strip pathnames for output */ 120 break; 121 default: 122 usage(); 123 } 124 } 125 argc -= optind; 126 argv += optind; 127 128 if (*argv != NULL) { 129 rval = 0; 130 do { 131 infp = fopen(infile = *argv, "r"); 132 if (infp == NULL) { 133 warn("%s", *argv); 134 rval = 1; 135 continue; 136 } 137 rval |= decode(); 138 fclose(infp); 139 } while (*++argv); 140 } else { 141 infile = "stdin"; 142 infp = stdin; 143 rval = decode(); 144 } 145 exit(rval); 146} 147 148static int 149decode(void) 150{ 151 int r, v; 152 153 if (rflag) { 154 /* relaxed alternative to decode2() */ 155 outfile = "/dev/stdout"; 156 outfp = stdout; 157 if (base64) 158 return (base64_decode()); 159 else 160 return (uu_decode()); 161 } 162 v = decode2(); 163 if (v == EOF) { 164 warnx("%s: missing or bad \"begin\" line", infile); 165 return (1); 166 } 167 for (r = v; cflag; r |= v) { 168 v = decode2(); 169 if (v == EOF) 170 break; 171 } 172 return (r); 173} 174 175static int 176decode2(void) 177{ 178 int flags, fd, mode; 179 size_t n, m; 180 char *p, *q; 181 void *handle; 182 struct passwd *pw; 183 struct stat st; 184 char buf[MAXPATHLEN + 1]; 185 186 base64 = 0; 187 /* search for header line */ 188 for (;;) { 189 if (fgets(buf, sizeof(buf), infp) == NULL) 190 return (EOF); 191 p = buf; 192 if (strncmp(p, "begin-base64 ", 13) == 0) { 193 base64 = 1; 194 p += 13; 195 } else if (strncmp(p, "begin ", 6) == 0) 196 p += 6; 197 else 198 continue; 199 /* p points to mode */ 200 q = strchr(p, ' '); 201 if (q == NULL) 202 continue; 203 *q++ = '\0'; 204 /* q points to filename */ 205 n = strlen(q); 206 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) 207 q[--n] = '\0'; 208 /* found valid header? */ 209 if (n > 0) 210 break; 211 } 212 213 handle = setmode(p); 214 if (handle == NULL) { 215 warnx("%s: unable to parse file mode", infile); 216 return (1); 217 } 218 mode = getmode(handle, 0) & 0666; 219 free(handle); 220 221 if (sflag) { 222 /* don't strip, so try ~user/file expansion */ 223 p = NULL; 224 pw = NULL; 225 if (*q == '~') 226 p = strchr(q, '/'); 227 if (p != NULL) { 228 *p = '\0'; 229 pw = getpwnam(q + 1); 230 *p = '/'; 231 } 232 if (pw != NULL) { 233 n = strlen(pw->pw_dir); 234 if (buf + n > p) { 235 /* make room */ 236 m = strlen(p); 237 if (sizeof(buf) < n + m) { 238 warnx("%s: bad output filename", 239 infile); 240 return (1); 241 } 242 p = memmove(buf + n, p, m); 243 } 244 q = memcpy(p - n, pw->pw_dir, n); 245 } 246 } else { 247 /* strip down to leaf name */ 248 p = strrchr(q, '/'); 249 if (p != NULL) 250 q = p + 1; 251 } 252 if (!oflag) 253 outfile = q; 254 255 /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ 256 if (pflag || strcmp(outfile, "/dev/stdout") == 0) 257 outfp = stdout; 258 else { 259 flags = O_WRONLY | O_CREAT | O_EXCL; 260 if (lstat(outfile, &st) == 0) { 261 if (iflag) { 262 warnc(EEXIST, "%s: %s", infile, outfile); 263 return (0); 264 } 265 switch (st.st_mode & S_IFMT) { 266 case S_IFREG: 267 case S_IFLNK: 268 /* avoid symlink attacks */ 269 if (unlink(outfile) == 0 || errno == ENOENT) 270 break; 271 warn("%s: unlink %s", infile, outfile); 272 return (1); 273 case S_IFDIR: 274 warnc(EISDIR, "%s: %s", infile, outfile); 275 return (1); 276 default: 277 if (oflag) { 278 /* trust command-line names */ 279 flags &= ~O_EXCL; 280 break; 281 } 282 warnc(EEXIST, "%s: %s", infile, outfile); 283 return (1); 284 } 285 } else if (errno != ENOENT) { 286 warn("%s: %s", infile, outfile); 287 return (1); 288 } 289 if ((fd = open(outfile, flags, mode)) < 0 || 290 (outfp = fdopen(fd, "w")) == NULL) { 291 warn("%s: %s", infile, outfile); 292 return (1); 293 } 294 } 295 296 if (base64) 297 return (base64_decode()); 298 else 299 return (uu_decode()); 300} 301 302static int 303getline(char *buf, size_t size) 304{ 305 306 if (fgets(buf, size, infp) != NULL) 307 return (2); 308 if (rflag) 309 return (0); 310 warnx("%s: %s: short file", infile, outfile); 311 return (1); 312} 313 314static int 315checkend(const char *ptr, const char *end, const char *msg) 316{ 317 size_t n; 318 319 n = strlen(end); 320 if (strncmp(ptr, end, n) != 0 || 321 strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { 322 warnx("%s: %s: %s", infile, outfile, msg); 323 return (1); 324 } 325 if (fclose(outfp) != 0) { 326 warn("%s: %s", infile, outfile); 327 return (1); 328 } 329 return (0); 330} 331 332static int 333uu_decode(void) 334{ 335 int i, ch; 336 char *p; 337 char buf[MAXPATHLEN+1]; 338 339 /* for each input line */ 340 for (;;) { 341 switch (getline(buf, sizeof(buf))) { 342 case 0: 343 return (0); 344 case 1: 345 return (1); 346 } 347 348#define DEC(c) (((c) - ' ') & 077) /* single character decode */ 349#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) 350 351#define OUT_OF_RANGE do { \ 352 warnx("%s: %s: character out of range: [%d-%d]", \ 353 infile, outfile, 1 + ' ', 077 + ' ' + 1); \ 354 return (1); \ 355} while (0) 356 357 /* 358 * `i' is used to avoid writing out all the characters 359 * at the end of the file. 360 */ 361 p = buf; 362 if ((i = DEC(*p)) <= 0) 363 break; 364 for (++p; i > 0; p += 4, i -= 3) 365 if (i >= 3) { 366 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && 367 IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) 368 OUT_OF_RANGE; 369 370 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 371 putc(ch, outfp); 372 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 373 putc(ch, outfp); 374 ch = DEC(p[2]) << 6 | DEC(p[3]); 375 putc(ch, outfp); 376 } else { 377 if (i >= 1) { 378 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) 379 OUT_OF_RANGE; 380 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; 381 putc(ch, outfp); 382 } 383 if (i >= 2) { 384 if (!(IS_DEC(*(p + 1)) && 385 IS_DEC(*(p + 2)))) 386 OUT_OF_RANGE; 387 388 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; 389 putc(ch, outfp); 390 } 391 if (i >= 3) { 392 if (!(IS_DEC(*(p + 2)) && 393 IS_DEC(*(p + 3)))) 394 OUT_OF_RANGE; 395 ch = DEC(p[2]) << 6 | DEC(p[3]); 396 putc(ch, outfp); 397 } 398 } 399 } 400 switch (getline(buf, sizeof(buf))) { 401 case 0: 402 return (0); 403 case 1: 404 return (1); 405 default: 406 return (checkend(buf, "end", "no \"end\" line")); 407 } 408} 409 410static int 411base64_decode(void) 412{ 413 int n, count, count4; 414 char inbuf[MAXPATHLEN + 1], *p; 415 unsigned char outbuf[MAXPATHLEN * 4]; 416 char leftover[MAXPATHLEN + 1]; 417 418 leftover[0] = '\0'; 419 for (;;) { 420 strcpy(inbuf, leftover); 421 switch (getline(inbuf + strlen(inbuf), 422 sizeof(inbuf) - strlen(inbuf))) { 423 case 0: 424 return (0); 425 case 1: 426 return (1); 427 } 428 429 count = 0; 430 count4 = -1; 431 p = inbuf; 432 while (*p != '\0') { 433 /* 434 * Base64 encoded strings have the following 435 * characters in them: A-Z, a-z, 0-9 and +, / and = 436 */ 437 if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=') 438 count++; 439 if (count % 4 == 0) 440 count4 = p - inbuf; 441 p++; 442 } 443 444 strcpy(leftover, inbuf + count4 + 1); 445 inbuf[count4 + 1] = 0; 446 447 n = b64_pton(inbuf, outbuf, sizeof(outbuf)); 448 449 if (n < 0) 450 break; 451 fwrite(outbuf, 1, n, outfp); 452 } 453 return (checkend(inbuf, "====", "error decoding base64 input stream")); 454} 455 456static void 457usage(void) 458{ 459 460 (void)fprintf(stderr, 461 "usage: uudecode [-cimprs] [file ...]\n" 462 " uudecode [-i] -o output_file [file]\n" 463 " b64decode [-cimprs] [file ...]\n" 464 " b64decode [-i] -o output_file [file]\n"); 465 exit(1); 466} 467