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