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