]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/bintrans/uudecode.c
Merge llvm-project release/15.x llvmorg-15.0.7-0-g8dfdcc7b7bf6
[FreeBSD/FreeBSD.git] / usr.bin / bintrans / uudecode.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1983, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #if 0
33 #ifndef lint
34 static const char copyright[] =
35 "@(#) Copyright (c) 1983, 1993\n\
36         The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38
39 #ifndef lint
40 static char sccsid[] = "@(#)uudecode.c  8.2 (Berkeley) 4/2/94";
41 #endif /* not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 /*
47  * uudecode [file ...]
48  *
49  * create the specified file, decoding as you go.
50  * used with uuencode.
51  */
52 #include <sys/param.h>
53 #include <sys/socket.h>
54 #include <sys/stat.h>
55
56 #include <netinet/in.h>
57
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <libgen.h>
63 #include <pwd.h>
64 #include <resolv.h>
65 #include <stdbool.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70
71 extern int main_decode(int, char *[]);
72 extern int main_base64_decode(const char *);
73
74 static const char *infile, *outfile;
75 static FILE *infp, *outfp;
76 static bool base64, cflag, iflag, oflag, pflag, rflag, sflag;
77
78 static void     usage(void);
79 static int      decode(void);
80 static int      decode2(void);
81 static int      uu_decode(void);
82 static int      base64_decode(void);
83
84 int
85 main_base64_decode(const char *in)
86 {
87         base64 = 1;
88         rflag = 1;
89         if (in != NULL) {
90                 infile = in;
91                 infp = fopen(infile, "r");
92                 if (infp == NULL)
93                         err(1, "%s", in);
94         } else {
95                 infile = "stdin";
96                 infp = stdin;
97         }
98         exit(decode());
99 }
100
101 int
102 main_decode(int argc, char *argv[])
103 {
104         int rval, ch;
105
106         if (strcmp(basename(argv[0]), "b64decode") == 0)
107                 base64 = true;
108
109         while ((ch = getopt(argc, argv, "cimo:prs")) != -1) {
110                 switch (ch) {
111                 case 'c':
112                         if (oflag || rflag)
113                                 usage();
114                         cflag = true; /* multiple uudecode'd files */
115                         break;
116                 case 'i':
117                         iflag = true; /* ask before override files */
118                         break;
119                 case 'm':
120                         base64 = true;
121                         break;
122                 case 'o':
123                         if (cflag || pflag || rflag || sflag)
124                                 usage();
125                         oflag = true; /* output to the specified file */
126                         sflag = true; /* do not strip pathnames for output */
127                         outfile = optarg; /* set the output filename */
128                         break;
129                 case 'p':
130                         if (oflag)
131                                 usage();
132                         pflag = true; /* print output to stdout */
133                         break;
134                 case 'r':
135                         if (cflag || oflag)
136                                 usage();
137                         rflag = true; /* decode raw data */
138                         break;
139                 case 's':
140                         if (oflag)
141                                 usage();
142                         sflag = true; /* do not strip pathnames for output */
143                         break;
144                 default:
145                         usage();
146                 }
147         }
148         argc -= optind;
149         argv += optind;
150
151         if (*argv != NULL) {
152                 rval = 0;
153                 do {
154                         infp = fopen(infile = *argv, "r");
155                         if (infp == NULL) {
156                                 warn("%s", *argv);
157                                 rval = 1;
158                                 continue;
159                         }
160                         rval |= decode();
161                         fclose(infp);
162                 } while (*++argv);
163         } else {
164                 infile = "stdin";
165                 infp = stdin;
166                 rval = decode();
167         }
168         exit(rval);
169 }
170
171 static int
172 decode(void)
173 {
174         int r, v;
175
176         if (rflag) {
177                 /* relaxed alternative to decode2() */
178                 outfile = "/dev/stdout";
179                 outfp = stdout;
180                 if (base64)
181                         return (base64_decode());
182                 else
183                         return (uu_decode());
184         }
185         v = decode2();
186         if (v == EOF) {
187                 warnx("%s: missing or bad \"begin\" line", infile);
188                 return (1);
189         }
190         for (r = v; cflag; r |= v) {
191                 v = decode2();
192                 if (v == EOF)
193                         break;
194         }
195         return (r);
196 }
197
198 static int
199 decode2(void)
200 {
201         int flags, fd, mode;
202         size_t n, m;
203         char *p, *q;
204         void *handle;
205         struct passwd *pw;
206         struct stat st;
207         char buf[MAXPATHLEN + 1];
208
209         base64 = false;
210         /* search for header line */
211         for (;;) {
212                 if (fgets(buf, sizeof(buf), infp) == NULL)
213                         return (EOF);
214                 p = buf;
215                 if (strncmp(p, "begin-base64 ", 13) == 0) {
216                         base64 = true;
217                         p += 13;
218                 } else if (strncmp(p, "begin ", 6) == 0)
219                         p += 6;
220                 else
221                         continue;
222                 /* p points to mode */
223                 q = strchr(p, ' ');
224                 if (q == NULL)
225                         continue;
226                 *q++ = '\0';
227                 /* q points to filename */
228                 n = strlen(q);
229                 while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r'))
230                         q[--n] = '\0';
231                 /* found valid header? */
232                 if (n > 0)
233                         break;
234         }
235
236         handle = setmode(p);
237         if (handle == NULL) {
238                 warnx("%s: unable to parse file mode", infile);
239                 return (1);
240         }
241         mode = getmode(handle, 0) & 0666;
242         free(handle);
243
244         if (sflag) {
245                 /* don't strip, so try ~user/file expansion */
246                 p = NULL;
247                 pw = NULL;
248                 if (*q == '~')
249                         p = strchr(q, '/');
250                 if (p != NULL) {
251                         *p = '\0';
252                         pw = getpwnam(q + 1);
253                         *p = '/';
254                 }
255                 if (pw != NULL) {
256                         n = strlen(pw->pw_dir);
257                         if (buf + n > p) {
258                                 /* make room */
259                                 m = strlen(p);
260                                 if (sizeof(buf) < n + m) {
261                                         warnx("%s: bad output filename",
262                                             infile);
263                                         return (1);
264                                 }
265                                 p = memmove(buf + n, p, m);
266                         }
267                         q = memcpy(p - n, pw->pw_dir, n);
268                 }
269         } else {
270                 /* strip down to leaf name */
271                 p = strrchr(q, '/');
272                 if (p != NULL)
273                         q = p + 1;
274         }
275         if (!oflag)
276                 outfile = q;
277
278         /* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */
279         if (pflag || strcmp(outfile, "/dev/stdout") == 0)
280                 outfp = stdout;
281         else {
282                 flags = O_WRONLY | O_CREAT | O_EXCL;
283                 if (lstat(outfile, &st) == 0) {
284                         if (iflag) {
285                                 warnc(EEXIST, "%s: %s", infile, outfile);
286                                 return (0);
287                         }
288                         switch (st.st_mode & S_IFMT) {
289                         case S_IFREG:
290                         case S_IFLNK:
291                                 /* avoid symlink attacks */
292                                 if (unlink(outfile) == 0 || errno == ENOENT)
293                                         break;
294                                 warn("%s: unlink %s", infile, outfile);
295                                 return (1);
296                         case S_IFDIR:
297                                 warnc(EISDIR, "%s: %s", infile, outfile);
298                                 return (1);
299                         default:
300                                 if (oflag) {
301                                         /* trust command-line names */
302                                         flags &= ~O_EXCL;
303                                         break;
304                                 }
305                                 warnc(EEXIST, "%s: %s", infile, outfile);
306                                 return (1);
307                         }
308                 } else if (errno != ENOENT) {
309                         warn("%s: %s", infile, outfile);
310                         return (1);
311                 }
312                 if ((fd = open(outfile, flags, mode)) < 0 ||
313                     (outfp = fdopen(fd, "w")) == NULL) {
314                         warn("%s: %s", infile, outfile);
315                         return (1);
316                 }
317         }
318
319         if (base64)
320                 return (base64_decode());
321         else
322                 return (uu_decode());
323 }
324
325 static int
326 get_line(char *buf, size_t size)
327 {
328
329         if (fgets(buf, size, infp) != NULL)
330                 return (2);
331         if (rflag)
332                 return (0);
333         warnx("%s: %s: short file", infile, outfile);
334         return (1);
335 }
336
337 static int
338 checkend(const char *ptr, const char *end, const char *msg)
339 {
340         size_t n;
341
342         n = strlen(end);
343         if (strncmp(ptr, end, n) != 0 ||
344             strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) {
345                 warnx("%s: %s: %s", infile, outfile, msg);
346                 return (1);
347         }
348         if (fclose(outfp) != 0) {
349                 warn("%s: %s", infile, outfile);
350                 return (1);
351         }
352         return (0);
353 }
354
355 static int
356 uu_decode(void)
357 {
358         int i, ch;
359         char *p;
360         char buf[MAXPATHLEN+1];
361
362         /* for each input line */
363         for (;;) {
364                 switch (get_line(buf, sizeof(buf))) {
365                 case 0:
366                         return (0);
367                 case 1:
368                         return (1);
369                 }
370
371 #define DEC(c)          (((c) - ' ') & 077)     /* single character decode */
372 #define IS_DEC(c)        ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) )
373
374 #define OUT_OF_RANGE do {                                               \
375         warnx("%s: %s: character out of range: [%d-%d]",                \
376             infile, outfile, ' ', 077 + ' ' + 1);                       \
377         return (1);                                                     \
378 } while (0)
379
380                 /*
381                  * `i' is used to avoid writing out all the characters
382                  * at the end of the file.
383                  */
384                 p = buf;
385                 if ((i = DEC(*p)) <= 0)
386                         break;
387                 for (++p; i > 0; p += 4, i -= 3)
388                         if (i >= 3) {
389                                 if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) &&
390                                     IS_DEC(*(p + 2)) && IS_DEC(*(p + 3))))
391                                         OUT_OF_RANGE;
392
393                                 ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
394                                 putc(ch, outfp);
395                                 ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
396                                 putc(ch, outfp);
397                                 ch = DEC(p[2]) << 6 | DEC(p[3]);
398                                 putc(ch, outfp);
399                         } else {
400                                 if (i >= 1) {
401                                         if (!(IS_DEC(*p) && IS_DEC(*(p + 1))))
402                                                 OUT_OF_RANGE;
403                                         ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
404                                         putc(ch, outfp);
405                                 }
406                                 if (i >= 2) {
407                                         if (!(IS_DEC(*(p + 1)) &&
408                                             IS_DEC(*(p + 2))))
409                                                 OUT_OF_RANGE;
410
411                                         ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
412                                         putc(ch, outfp);
413                                 }
414                                 if (i >= 3) {
415                                         if (!(IS_DEC(*(p + 2)) &&
416                                             IS_DEC(*(p + 3))))
417                                                 OUT_OF_RANGE;
418                                         ch = DEC(p[2]) << 6 | DEC(p[3]);
419                                         putc(ch, outfp);
420                                 }
421                         }
422         }
423         switch (get_line(buf, sizeof(buf))) {
424         case 0:
425                 return (0);
426         case 1:
427                 return (1);
428         default:
429                 return (checkend(buf, "end", "no \"end\" line"));
430         }
431 }
432
433 static int
434 base64_decode(void)
435 {
436         int n, count, count4;
437         char inbuf[MAXPATHLEN + 1], *p;
438         unsigned char outbuf[MAXPATHLEN * 4];
439         char leftover[MAXPATHLEN + 1];
440
441         leftover[0] = '\0';
442         for (;;) {
443                 strcpy(inbuf, leftover);
444                 switch (get_line(inbuf + strlen(inbuf),
445                     sizeof(inbuf) - strlen(inbuf))) {
446                 case 0:
447                         return (0);
448                 case 1:
449                         return (1);
450                 }
451
452                 count = 0;
453                 count4 = -1;
454                 p = inbuf;
455                 while (*p != '\0') {
456                         /*
457                          * Base64 encoded strings have the following
458                          * characters in them: A-Z, a-z, 0-9 and +, / and =
459                          */
460                         if (isalnum(*p) || *p == '+' || *p == '/' || *p == '=')
461                                 count++;
462                         if (count % 4 == 0)
463                                 count4 = p - inbuf;
464                         p++;
465                 }
466
467                 strcpy(leftover, inbuf + count4 + 1);
468                 inbuf[count4 + 1] = 0;
469
470                 n = b64_pton(inbuf, outbuf, sizeof(outbuf));
471
472                 if (n < 0)
473                         break;
474                 fwrite(outbuf, 1, n, outfp);
475         }
476         return (checkend(inbuf, "====", "error decoding base64 input stream"));
477 }
478
479 static void
480 usage(void)
481 {
482
483         (void)fprintf(stderr,
484             "usage: uudecode [-cimprs] [file ...]\n"
485             "       uudecode [-i] -o output_file [file]\n"
486             "       b64decode [-cimprs] [file ...]\n"
487             "       b64decode [-i] -o output_file [file]\n");
488         exit(1);
489 }