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