Annotation of src/usr.bin/uudecode/uudecode.c, Revision 1.18
1.18 ! fgsch 1: /* $OpenBSD: uudecode.c,v 1.17 2009/10/27 23:59:46 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:
1.1 deraadt 38: #include <sys/param.h>
1.14 millert 39: #include <sys/socket.h>
1.1 deraadt 40: #include <sys/stat.h>
41:
1.14 millert 42: #include <netinet/in.h>
43:
44: #include <err.h>
45: #include <errno.h>
46: #include <fcntl.h>
47: #include <locale.h>
1.1 deraadt 48: #include <pwd.h>
1.14 millert 49: #include <resolv.h>
50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <string.h>
1.1 deraadt 53: #include <unistd.h>
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.1 deraadt 185: char buf[MAXPATHLEN];
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) {
263: errno = EEXIST;
264: warn("%s: %s", infile, outfile);
265: return (0);
266: }
267: switch (st.st_mode & S_IFMT) {
268: case S_IFREG:
269: case S_IFLNK:
270: /* avoid symlink attacks */
271: if (unlink(outfile) == 0 || errno == ENOENT)
272: break;
273: warn("%s: unlink %s", infile, outfile);
274: return (1);
275: case S_IFDIR:
276: errno = EISDIR;
277: warn("%s: %s", infile, outfile);
278: return (1);
279: default:
280: if (oflag) {
281: /* trust command-line names */
282: flags &= ~(O_EXCL|O_NOFOLLOW);
283: break;
284: }
285: errno = EEXIST;
286: warn("%s: %s", infile, outfile);
287: return (1);
288: }
289: } else if (errno != ENOENT) {
290: warn("%s: %s", infile, outfile);
291: return (1);
292: }
293: if ((fd = open(outfile, flags, mode)) < 0 ||
294: (outfp = fdopen(fd, "w")) == NULL) {
295: warn("%s: %s", infile, outfile);
296: return (1);
1.6 dgregor 297: }
1.1 deraadt 298: }
299:
1.14 millert 300: if (base64)
301: return (base64_decode());
302: else
303: return (uu_decode());
304: }
305:
306: static int
1.18 ! fgsch 307: get_line(char *buf, size_t size)
1.14 millert 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:
317: static int
318: checkend(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:
335: static int
336: uu_decode(void)
337: {
338: int i, ch;
339: char *p;
340: char buf[MAXPATHLEN];
341:
1.1 deraadt 342: /* for each input line */
343: for (;;) {
1.18 ! fgsch 344: switch (get_line(buf, sizeof(buf))) {
1.14 millert 345: case 0:
346: return (0);
347: case 1:
348: return (1);
1.1 deraadt 349: }
1.14 millert 350:
1.1 deraadt 351: #define DEC(c) (((c) - ' ') & 077) /* single character decode */
1.14 millert 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:
1.1 deraadt 360: /*
1.14 millert 361: * `i' is used to avoid writing out all the characters
1.1 deraadt 362: * at the end of the file.
363: */
1.14 millert 364: p = buf;
365: if ((i = DEC(*p)) <= 0)
1.1 deraadt 366: break;
1.14 millert 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:
1.1 deraadt 373: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 374: putc(ch, outfp);
1.1 deraadt 375: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 376: putc(ch, outfp);
1.1 deraadt 377: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 378: putc(ch, outfp);
1.1 deraadt 379: }
380: else {
1.14 millert 381: if (i >= 1) {
382: if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
383: OUT_OF_RANGE;
1.1 deraadt 384: ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
1.14 millert 385: putc(ch, outfp);
1.1 deraadt 386: }
1.14 millert 387: if (i >= 2) {
388: if (!(IS_DEC(*(p + 1)) &&
389: IS_DEC(*(p + 2))))
390: OUT_OF_RANGE;
391:
1.1 deraadt 392: ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
1.14 millert 393: putc(ch, outfp);
1.1 deraadt 394: }
1.14 millert 395: if (i >= 3) {
396: if (!(IS_DEC(*(p + 2)) &&
397: IS_DEC(*(p + 3))))
398: OUT_OF_RANGE;
1.1 deraadt 399: ch = DEC(p[2]) << 6 | DEC(p[3]);
1.14 millert 400: putc(ch, outfp);
1.1 deraadt 401: }
402: }
403: }
1.18 ! fgsch 404: switch (get_line(buf, sizeof(buf))) {
1.14 millert 405: case 0:
406: return (0);
407: case 1:
408: return (1);
409: default:
410: return (checkend(buf, "end", "no \"end\" line"));
411: }
412: }
413:
414: static int
415: base64_decode(void)
416: {
417: int n;
418: char inbuf[MAXPATHLEN];
419: unsigned char outbuf[MAXPATHLEN * 4];
420:
421: for (;;) {
1.18 ! fgsch 422: switch (get_line(inbuf, sizeof(inbuf))) {
1.14 millert 423: case 0:
424: return (0);
425: case 1:
426: return (1);
427: }
428: n = b64_pton(inbuf, outbuf, sizeof(outbuf));
429: if (n < 0)
430: break;
431: fwrite(outbuf, 1, n, outfp);
1.1 deraadt 432: }
1.14 millert 433: return (checkend(inbuf, "====",
434: "error decoding base64 input stream"));
1.1 deraadt 435: }
436:
437: static void
1.16 sobrado 438: usage(void)
1.1 deraadt 439: {
1.16 sobrado 440: switch (pmode) {
1.15 sobrado 441: case MODE_DECODE:
442: (void)fprintf(stderr,
443: "usage: uudecode [-cimprs] [file ...]\n"
444: " uudecode [-i] -o output_file [file]\n");
445: break;
446: case MODE_B64DECODE:
447: (void)fprintf(stderr,
448: "usage: b64decode [-ciprs] [file ...]\n"
449: " b64decode [-i] -o output_file [file]\n");
450: break;
451: }
1.1 deraadt 452: exit(1);
453: }