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