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