Annotation of src/usr.bin/uudecode/uudecode.c, Revision 1.22
1.22 ! deraadt 1: /* $OpenBSD: uudecode.c,v 1.21 2015/10/07 06:00:33 deraadt 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;
1.21 deraadt 128:
129: if (oflag || 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: }
154: exit(rval);
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: }
298: if ((fd = open(outfile, flags, mode)) < 0 ||
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:
359: #define OUT_OF_RANGE do { \
360: warnx("%s: %s: character out of range: [%d-%d]", \
361: infile, outfile, 1 + ' ', 077 + ' ' + 1); \
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) {
374: if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
375: IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
376: OUT_OF_RANGE;
377:
1.1 deraadt 378: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 379: putc(ch, outfp);
1.1 deraadt 380: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 381: putc(ch, outfp);
1.1 deraadt 382: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 383: putc(ch, outfp);
1.1 deraadt 384: }
385: else {
1.14 millert 386: if (i >= 1) {
387: if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
388: OUT_OF_RANGE;
1.1 deraadt 389: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 390: putc(ch, outfp);
1.1 deraadt 391: }
1.14 millert 392: if (i >= 2) {
393: if (!(IS_DEC(*(p + 1)) &&
394: IS_DEC(*(p + 2))))
395: OUT_OF_RANGE;
396:
1.1 deraadt 397: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 398: putc(ch, outfp);
1.1 deraadt 399: }
1.14 millert 400: if (i >= 3) {
401: if (!(IS_DEC(*(p + 2)) &&
402: IS_DEC(*(p + 3))))
403: OUT_OF_RANGE;
1.1 deraadt 404: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 405: putc(ch, outfp);
1.1 deraadt 406: }
407: }
408: }
1.18 fgsch 409: switch (get_line(buf, sizeof(buf))) {
1.14 millert 410: case 0:
411: return (0);
412: case 1:
413: return (1);
414: default:
415: return (checkend(buf, "end", "no \"end\" line"));
416: }
417: }
418:
419: static int
420: base64_decode(void)
421: {
422: int n;
1.20 deraadt 423: char inbuf[PATH_MAX];
424: unsigned char outbuf[PATH_MAX * 4];
1.14 millert 425:
426: for (;;) {
1.18 fgsch 427: switch (get_line(inbuf, sizeof(inbuf))) {
1.14 millert 428: case 0:
429: return (0);
430: case 1:
431: return (1);
432: }
433: n = b64_pton(inbuf, outbuf, sizeof(outbuf));
434: if (n < 0)
435: break;
436: fwrite(outbuf, 1, n, outfp);
1.1 deraadt 437: }
1.14 millert 438: return (checkend(inbuf, "====",
439: "error decoding base64 input stream"));
1.1 deraadt 440: }
441:
442: static void
1.16 sobrado 443: usage(void)
1.1 deraadt 444: {
1.16 sobrado 445: switch (pmode) {
1.15 sobrado 446: case MODE_DECODE:
447: (void)fprintf(stderr,
448: "usage: uudecode [-cimprs] [file ...]\n"
449: " uudecode [-i] -o output_file [file]\n");
450: break;
451: case MODE_B64DECODE:
452: (void)fprintf(stderr,
453: "usage: b64decode [-ciprs] [file ...]\n"
454: " b64decode [-i] -o output_file [file]\n");
455: break;
456: }
1.1 deraadt 457: exit(1);
458: }