Annotation of src/usr.bin/uudecode/uudecode.c, Revision 1.20
1.20 ! deraadt 1: /* $OpenBSD: uudecode.c,v 1.19 2014/05/20 01:25:23 guenther Exp $ */
1.14 millert 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:
33: /*
1.14 millert 34: * Create the specified file, decoding as you go.
35: * Used with uuencode.
1.1 deraadt 36: */
1.14 millert 37:
38: #include <sys/socket.h>
1.1 deraadt 39: #include <sys/stat.h>
40:
1.14 millert 41: #include <netinet/in.h>
42:
43: #include <err.h>
44: #include <errno.h>
45: #include <fcntl.h>
46: #include <locale.h>
1.1 deraadt 47: #include <pwd.h>
1.14 millert 48: #include <resolv.h>
49: #include <stdio.h>
50: #include <stdlib.h>
51: #include <string.h>
1.1 deraadt 52: #include <unistd.h>
1.20 ! deraadt 53: #include <limits.h>
1.1 deraadt 54:
1.14 millert 55: static const char *infile, *outfile;
56: static FILE *infp, *outfp;
57: static int base64, cflag, iflag, oflag, pflag, rflag, sflag;
58:
1.16 sobrado 59: static void usage(void);
1.14 millert 60: static int decode(void);
61: static int decode2(void);
62: static int uu_decode(void);
63: static int base64_decode(void);
1.1 deraadt 64:
1.16 sobrado 65: enum program_mode {
66: MODE_DECODE,
67: MODE_B64DECODE
68: } pmode;
1.15 sobrado 69:
1.1 deraadt 70: int
1.11 deraadt 71: main(int argc, char *argv[])
1.1 deraadt 72: {
1.16 sobrado 73: int rval, ch;
1.14 millert 74: extern char *__progname;
1.15 sobrado 75: static const char *optstr[2] = {
76: "cimo:prs",
77: "cio:prs"
78: };
1.14 millert 79:
1.16 sobrado 80: pmode = MODE_DECODE;
1.15 sobrado 81: if (strcmp(__progname, "b64decode") == 0) {
1.14 millert 82: base64 = 1;
1.16 sobrado 83: pmode = MODE_B64DECODE;
1.15 sobrado 84: }
1.1 deraadt 85:
86: setlocale(LC_ALL, "");
1.16 sobrado 87: while ((ch = getopt(argc, argv, optstr[pmode])) != -1) {
1.14 millert 88: switch(ch) {
89: case 'c':
90: if (oflag || rflag)
1.16 sobrado 91: usage();
1.14 millert 92: cflag = 1; /* multiple uudecode'd files */
93: break;
94: case 'i':
95: iflag = 1; /* ask before override files */
96: break;
97: case 'm':
98: base64 = 1;
99: break;
100: case 'o':
101: if (cflag || pflag || rflag || sflag)
1.16 sobrado 102: usage();
1.14 millert 103: oflag = 1; /* output to the specified file */
104: sflag = 1; /* do not strip pathnames for output */
105: outfile = optarg; /* set the output filename */
106: break;
1.6 dgregor 107: case 'p':
1.14 millert 108: if (oflag)
1.16 sobrado 109: usage();
1.14 millert 110: pflag = 1; /* print output to stdout */
111: break;
112: case 'r':
113: if (cflag || oflag)
1.16 sobrado 114: usage();
1.14 millert 115: rflag = 1; /* decode raw data */
116: break;
117: case 's':
118: if (oflag)
1.16 sobrado 119: usage();
1.14 millert 120: sflag = 1; /* do not strip pathnames for output */
1.6 dgregor 121: break;
122: default:
1.16 sobrado 123: usage();
1.6 dgregor 124: }
1.14 millert 125: }
1.1 deraadt 126: argc -= optind;
127: argv += optind;
128:
129: if (*argv) {
130: rval = 0;
131: do {
1.14 millert 132: infp = fopen(infile = *argv, "r");
133: if (infp == NULL) {
1.13 mickey 134: warn("%s", *argv);
1.1 deraadt 135: rval = 1;
136: continue;
137: }
1.14 millert 138: rval |= decode();
139: fclose(infp);
1.1 deraadt 140: } while (*++argv);
141: } else {
1.14 millert 142: infile = "stdin";
143: infp = stdin;
144: rval = decode();
1.1 deraadt 145: }
146: exit(rval);
147: }
148:
149: static int
1.14 millert 150: decode(void)
1.1 deraadt 151: {
1.14 millert 152: int r, v;
153:
154: if (rflag) {
155: /* relaxed alternative to decode2() */
156: outfile = "/dev/stdout";
157: outfp = stdout;
158: if (base64)
159: return (base64_decode());
160: else
161: return (uu_decode());
162: }
163: v = decode2();
164: if (v == EOF) {
165: warnx("%s: missing or bad \"begin\" line", infile);
166: return (1);
167: }
168: for (r = v; cflag; r |= v) {
169: v = decode2();
170: if (v == EOF)
171: break;
172: }
173: return (r);
174: }
175:
176: static int
177: decode2(void)
178: {
179: int flags, fd, mode;
180: size_t n, m;
181: char *p, *q;
182: void *handle;
1.1 deraadt 183: struct passwd *pw;
1.14 millert 184: struct stat st;
1.20 ! deraadt 185: char buf[PATH_MAX];
1.1 deraadt 186:
1.14 millert 187: base64 = 0;
1.1 deraadt 188: /* search for header line */
1.14 millert 189: for (;;) {
190: if (fgets(buf, sizeof(buf), infp) == NULL)
191: return (EOF);
192: p = buf;
193: if (strncmp(p, "begin-base64 ", 13) == 0) {
194: base64 = 1;
195: p += 13;
196: } else if (strncmp(p, "begin ", 6) == 0)
197: p += 6;
198: else
199: continue;
200: /* p points to mode */
201: q = strchr(p, ' ');
202: if (q == NULL)
203: continue;
204: *q++ = '\0';
205: /* q points to filename */
206: n = strlen(q);
207: while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
208: q[--n] = '\0';
209: /* found valid header? */
210: if (n > 0)
211: break;
212: }
213:
214: handle = setmode(p);
215: if (handle == NULL) {
216: warnx("%s: unable to parse file mode", infile);
217: return (1);
218: }
219: mode = getmode(handle, 0) & 0666;
220: free(handle);
221:
222: if (sflag) {
223: /* don't strip, so try ~user/file expansion */
224: p = NULL;
225: pw = NULL;
226: if (*q == '~')
227: p = strchr(q, '/');
228: if (p != NULL) {
229: *p = '\0';
230: pw = getpwnam(q + 1);
231: *p = '/';
232: }
233: if (pw != NULL) {
234: n = strlen(pw->pw_dir);
235: if (buf + n > p) {
236: /* make room */
237: m = strlen(p);
238: if (sizeof(buf) < n + m) {
239: warnx("%s: bad output filename",
240: infile);
241: return (1);
242: }
243: p = memmove(buf + n, p, m);
244: }
245: q = memcpy(p - n, pw->pw_dir, n);
246: }
247: } else {
248: /* strip down to leaf name */
249: p = strrchr(q, '/');
250: if (p != NULL)
251: q = p + 1;
252: }
253: if (!oflag)
254: outfile = q;
255:
256: /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
257: if (pflag || strcmp(outfile, "/dev/stdout") == 0)
258: outfp = stdout;
259: else {
260: flags = O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW;
261: if (lstat(outfile, &st) == 0) {
262: if (iflag) {
1.19 guenther 263: warnc(EEXIST, "%s: %s", infile, outfile);
1.14 millert 264: return (0);
265: }
266: switch (st.st_mode & S_IFMT) {
267: case S_IFREG:
268: case S_IFLNK:
269: /* avoid symlink attacks */
270: if (unlink(outfile) == 0 || errno == ENOENT)
271: break;
272: warn("%s: unlink %s", infile, outfile);
273: return (1);
274: case S_IFDIR:
1.19 guenther 275: warnc(EISDIR, "%s: %s", infile, outfile);
1.14 millert 276: return (1);
277: default:
278: if (oflag) {
279: /* trust command-line names */
280: flags &= ~(O_EXCL|O_NOFOLLOW);
281: break;
282: }
1.19 guenther 283: warnc(EEXIST, "%s: %s", infile, outfile);
1.14 millert 284: return (1);
285: }
286: } else if (errno != ENOENT) {
287: warn("%s: %s", infile, outfile);
288: return (1);
289: }
290: if ((fd = open(outfile, flags, mode)) < 0 ||
291: (outfp = fdopen(fd, "w")) == NULL) {
292: warn("%s: %s", infile, outfile);
293: return (1);
1.6 dgregor 294: }
1.1 deraadt 295: }
296:
1.14 millert 297: if (base64)
298: return (base64_decode());
299: else
300: return (uu_decode());
301: }
302:
303: static int
1.18 fgsch 304: get_line(char *buf, size_t size)
1.14 millert 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:
314: static int
315: checkend(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:
332: static int
333: uu_decode(void)
334: {
335: int i, ch;
336: char *p;
1.20 ! deraadt 337: char buf[PATH_MAX];
1.14 millert 338:
1.1 deraadt 339: /* for each input line */
340: for (;;) {
1.18 fgsch 341: switch (get_line(buf, sizeof(buf))) {
1.14 millert 342: case 0:
343: return (0);
344: case 1:
345: return (1);
1.1 deraadt 346: }
1.14 millert 347:
1.1 deraadt 348: #define DEC(c) (((c) - ' ') & 077) /* single character decode */
1.14 millert 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:
1.1 deraadt 357: /*
1.14 millert 358: * `i' is used to avoid writing out all the characters
1.1 deraadt 359: * at the end of the file.
360: */
1.14 millert 361: p = buf;
362: if ((i = DEC(*p)) <= 0)
1.1 deraadt 363: break;
1.14 millert 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:
1.1 deraadt 370: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 371: putc(ch, outfp);
1.1 deraadt 372: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 373: putc(ch, outfp);
1.1 deraadt 374: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 375: putc(ch, outfp);
1.1 deraadt 376: }
377: else {
1.14 millert 378: if (i >= 1) {
379: if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
380: OUT_OF_RANGE;
1.1 deraadt 381: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 382: putc(ch, outfp);
1.1 deraadt 383: }
1.14 millert 384: if (i >= 2) {
385: if (!(IS_DEC(*(p + 1)) &&
386: IS_DEC(*(p + 2))))
387: OUT_OF_RANGE;
388:
1.1 deraadt 389: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 390: putc(ch, outfp);
1.1 deraadt 391: }
1.14 millert 392: if (i >= 3) {
393: if (!(IS_DEC(*(p + 2)) &&
394: IS_DEC(*(p + 3))))
395: OUT_OF_RANGE;
1.1 deraadt 396: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 397: putc(ch, outfp);
1.1 deraadt 398: }
399: }
400: }
1.18 fgsch 401: switch (get_line(buf, sizeof(buf))) {
1.14 millert 402: case 0:
403: return (0);
404: case 1:
405: return (1);
406: default:
407: return (checkend(buf, "end", "no \"end\" line"));
408: }
409: }
410:
411: static int
412: base64_decode(void)
413: {
414: int n;
1.20 ! deraadt 415: char inbuf[PATH_MAX];
! 416: unsigned char outbuf[PATH_MAX * 4];
1.14 millert 417:
418: for (;;) {
1.18 fgsch 419: switch (get_line(inbuf, sizeof(inbuf))) {
1.14 millert 420: case 0:
421: return (0);
422: case 1:
423: return (1);
424: }
425: n = b64_pton(inbuf, outbuf, sizeof(outbuf));
426: if (n < 0)
427: break;
428: fwrite(outbuf, 1, n, outfp);
1.1 deraadt 429: }
1.14 millert 430: return (checkend(inbuf, "====",
431: "error decoding base64 input stream"));
1.1 deraadt 432: }
433:
434: static void
1.16 sobrado 435: usage(void)
1.1 deraadt 436: {
1.16 sobrado 437: switch (pmode) {
1.15 sobrado 438: case MODE_DECODE:
439: (void)fprintf(stderr,
440: "usage: uudecode [-cimprs] [file ...]\n"
441: " uudecode [-i] -o output_file [file]\n");
442: break;
443: case MODE_B64DECODE:
444: (void)fprintf(stderr,
445: "usage: b64decode [-ciprs] [file ...]\n"
446: " b64decode [-i] -o output_file [file]\n");
447: break;
448: }
1.1 deraadt 449: exit(1);
450: }