GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: uudecode.c,v 1.23 2016/01/03 14:43:20 tb Exp $ */ |
||
2 |
/* $FreeBSD: uudecode.c,v 1.49 2003/05/03 19:44:46 obrien Exp $ */ |
||
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. |
||
16 |
* 3. Neither the name of the University nor the names of its contributors |
||
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 |
/* |
||
34 |
* Create the specified file, decoding as you go. |
||
35 |
* Used with uuencode. |
||
36 |
*/ |
||
37 |
|||
38 |
#include <sys/socket.h> |
||
39 |
#include <sys/stat.h> |
||
40 |
|||
41 |
#include <netinet/in.h> |
||
42 |
|||
43 |
#include <err.h> |
||
44 |
#include <errno.h> |
||
45 |
#include <fcntl.h> |
||
46 |
#include <locale.h> |
||
47 |
#include <pwd.h> |
||
48 |
#include <resolv.h> |
||
49 |
#include <stdio.h> |
||
50 |
#include <stdlib.h> |
||
51 |
#include <string.h> |
||
52 |
#include <unistd.h> |
||
53 |
#include <limits.h> |
||
54 |
|||
55 |
static const char *infile, *outfile; |
||
56 |
static FILE *infp, *outfp; |
||
57 |
static int base64, cflag, iflag, oflag, pflag, rflag, sflag; |
||
58 |
|||
59 |
static void usage(void); |
||
60 |
static int decode(void); |
||
61 |
static int decode2(void); |
||
62 |
static int uu_decode(void); |
||
63 |
static int base64_decode(void); |
||
64 |
|||
65 |
enum program_mode { |
||
66 |
MODE_DECODE, |
||
67 |
MODE_B64DECODE |
||
68 |
} pmode; |
||
69 |
|||
70 |
int |
||
71 |
main(int argc, char *argv[]) |
||
72 |
{ |
||
73 |
int rval, ch; |
||
74 |
extern char *__progname; |
||
75 |
static const char *optstr[2] = { |
||
76 |
"cimo:prs", |
||
77 |
"cio:prs" |
||
78 |
}; |
||
79 |
|||
80 |
204 |
pmode = MODE_DECODE; |
|
81 |
✗✓ | 102 |
if (strcmp(__progname, "b64decode") == 0) { |
82 |
base64 = 1; |
||
83 |
pmode = MODE_B64DECODE; |
||
84 |
} |
||
85 |
|||
86 |
102 |
setlocale(LC_ALL, ""); |
|
87 |
✓✓ | 296 |
while ((ch = getopt(argc, argv, optstr[pmode])) != -1) { |
88 |
✗✗✗✓ ✓✗✗✗ |
92 |
switch(ch) { |
89 |
case 'c': |
||
90 |
if (oflag || rflag) |
||
91 |
usage(); |
||
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 |
✗✓ | 23 |
if (cflag || pflag || rflag || sflag) |
102 |
usage(); |
||
103 |
23 |
oflag = 1; /* output to the specified file */ |
|
104 |
23 |
sflag = 1; /* do not strip pathnames for output */ |
|
105 |
23 |
outfile = optarg; /* set the output filename */ |
|
106 |
23 |
break; |
|
107 |
case 'p': |
||
108 |
✗✓ | 69 |
if (oflag) |
109 |
usage(); |
||
110 |
69 |
pflag = 1; /* print output to stdout */ |
|
111 |
69 |
break; |
|
112 |
case 'r': |
||
113 |
if (cflag || oflag) |
||
114 |
usage(); |
||
115 |
rflag = 1; /* decode raw data */ |
||
116 |
break; |
||
117 |
case 's': |
||
118 |
if (oflag) |
||
119 |
usage(); |
||
120 |
sflag = 1; /* do not strip pathnames for output */ |
||
121 |
break; |
||
122 |
default: |
||
123 |
usage(); |
||
124 |
} |
||
125 |
} |
||
126 |
102 |
argc -= optind; |
|
127 |
102 |
argv += optind; |
|
128 |
|||
129 |
✓✓ | 102 |
if (sflag) { |
130 |
✗✓ | 23 |
if (pledge("stdio rpath wpath cpath getpw flock", NULL) == -1) |
131 |
err(1, "pledge"); |
||
132 |
✓✓ | 79 |
} else if (pflag == 0) { |
133 |
✗✓ | 10 |
if (pledge("stdio rpath wpath cpath flock", NULL) == -1) |
134 |
err(1, "pledge"); |
||
135 |
} else { |
||
136 |
✗✓ | 69 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
137 |
err(1, "pledge"); |
||
138 |
} |
||
139 |
|||
140 |
✓✓ | 102 |
if (*argv) { |
141 |
rval = 0; |
||
142 |
10 |
do { |
|
143 |
10 |
infp = fopen(infile = *argv, "r"); |
|
144 |
✗✓ | 10 |
if (infp == NULL) { |
145 |
warn("%s", *argv); |
||
146 |
rval = 1; |
||
147 |
continue; |
||
148 |
} |
||
149 |
10 |
rval |= decode(); |
|
150 |
10 |
fclose(infp); |
|
151 |
✗✓ | 20 |
} while (*++argv); |
152 |
} else { |
||
153 |
92 |
infile = "stdin"; |
|
154 |
92 |
infp = stdin; |
|
155 |
92 |
rval = decode(); |
|
156 |
} |
||
157 |
exit(rval); |
||
158 |
} |
||
159 |
|||
160 |
static int |
||
161 |
decode(void) |
||
162 |
{ |
||
163 |
int r, v; |
||
164 |
|||
165 |
✗✓ | 204 |
if (rflag) { |
166 |
/* relaxed alternative to decode2() */ |
||
167 |
outfile = "/dev/stdout"; |
||
168 |
outfp = stdout; |
||
169 |
if (base64) |
||
170 |
return (base64_decode()); |
||
171 |
else |
||
172 |
return (uu_decode()); |
||
173 |
} |
||
174 |
102 |
v = decode2(); |
|
175 |
✗✓ | 102 |
if (v == EOF) { |
176 |
warnx("%s: missing or bad \"begin\" line", infile); |
||
177 |
return (1); |
||
178 |
} |
||
179 |
✗✓ | 204 |
for (r = v; cflag; r |= v) { |
180 |
v = decode2(); |
||
181 |
if (v == EOF) |
||
182 |
break; |
||
183 |
} |
||
184 |
102 |
return (r); |
|
185 |
102 |
} |
|
186 |
|||
187 |
static int |
||
188 |
decode2(void) |
||
189 |
{ |
||
190 |
int flags, fd, mode; |
||
191 |
size_t n, m; |
||
192 |
char *p, *q; |
||
193 |
void *handle; |
||
194 |
struct passwd *pw; |
||
195 |
204 |
struct stat st; |
|
196 |
102 |
char buf[PATH_MAX]; |
|
197 |
|||
198 |
102 |
base64 = 0; |
|
199 |
/* search for header line */ |
||
200 |
102 |
for (;;) { |
|
201 |
✗✓ | 127 |
if (fgets(buf, sizeof(buf), infp) == NULL) |
202 |
return (EOF); |
||
203 |
p = buf; |
||
204 |
✗✓ | 127 |
if (strncmp(p, "begin-base64 ", 13) == 0) { |
205 |
base64 = 1; |
||
206 |
p += 13; |
||
207 |
✓✓ | 127 |
} else if (strncmp(p, "begin ", 6) == 0) |
208 |
102 |
p += 6; |
|
209 |
else |
||
210 |
continue; |
||
211 |
/* p points to mode */ |
||
212 |
102 |
q = strchr(p, ' '); |
|
213 |
✗✓ | 102 |
if (q == NULL) |
214 |
continue; |
||
215 |
102 |
*q++ = '\0'; |
|
216 |
/* q points to filename */ |
||
217 |
102 |
n = strlen(q); |
|
218 |
✓✗✓✓ ✗✓ |
714 |
while (n > 0 && (q[n-1] == '\n' || q[n-1] == '\r')) |
219 |
102 |
q[--n] = '\0'; |
|
220 |
/* found valid header? */ |
||
221 |
✗✓ | 102 |
if (n > 0) |
222 |
break; |
||
223 |
} |
||
224 |
|||
225 |
102 |
handle = setmode(p); |
|
226 |
✗✓ | 102 |
if (handle == NULL) { |
227 |
warnx("%s: unable to parse file mode", infile); |
||
228 |
return (1); |
||
229 |
} |
||
230 |
102 |
mode = getmode(handle, 0) & 0666; |
|
231 |
102 |
free(handle); |
|
232 |
|||
233 |
✓✓ | 102 |
if (sflag) { |
234 |
/* don't strip, so try ~user/file expansion */ |
||
235 |
p = NULL; |
||
236 |
pw = NULL; |
||
237 |
✗✓ | 23 |
if (*q == '~') |
238 |
p = strchr(q, '/'); |
||
239 |
✗✓ | 23 |
if (p != NULL) { |
240 |
*p = '\0'; |
||
241 |
pw = getpwnam(q + 1); |
||
242 |
*p = '/'; |
||
243 |
} |
||
244 |
✗✓ | 23 |
if (pw != NULL) { |
245 |
n = strlen(pw->pw_dir); |
||
246 |
if (buf + n > p) { |
||
247 |
/* make room */ |
||
248 |
m = strlen(p); |
||
249 |
if (sizeof(buf) < n + m) { |
||
250 |
warnx("%s: bad output filename", |
||
251 |
infile); |
||
252 |
return (1); |
||
253 |
} |
||
254 |
p = memmove(buf + n, p, m); |
||
255 |
} |
||
256 |
q = memcpy(p - n, pw->pw_dir, n); |
||
257 |
} |
||
258 |
} else { |
||
259 |
/* strip down to leaf name */ |
||
260 |
79 |
p = strrchr(q, '/'); |
|
261 |
✗✓ | 79 |
if (p != NULL) |
262 |
q = p + 1; |
||
263 |
} |
||
264 |
✓✓ | 102 |
if (!oflag) |
265 |
79 |
outfile = q; |
|
266 |
|||
267 |
/* POSIX says "/dev/stdout" is a 'magic cookie' not a special file. */ |
||
268 |
✓✓✗✓ |
135 |
if (pflag || strcmp(outfile, "/dev/stdout") == 0) |
269 |
69 |
outfp = stdout; |
|
270 |
else { |
||
271 |
flags = O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW; |
||
272 |
✓✓ | 33 |
if (lstat(outfile, &st) == 0) { |
273 |
✗✓ | 16 |
if (iflag) { |
274 |
warnc(EEXIST, "%s: %s", infile, outfile); |
||
275 |
return (0); |
||
276 |
} |
||
277 |
✗✓✗✗ |
16 |
switch (st.st_mode & S_IFMT) { |
278 |
case S_IFREG: |
||
279 |
case S_IFLNK: |
||
280 |
/* avoid symlink attacks */ |
||
281 |
✗✓✗✗ |
16 |
if (unlink(outfile) == 0 || errno == ENOENT) |
282 |
break; |
||
283 |
warn("%s: unlink %s", infile, outfile); |
||
284 |
return (1); |
||
285 |
case S_IFDIR: |
||
286 |
warnc(EISDIR, "%s: %s", infile, outfile); |
||
287 |
return (1); |
||
288 |
default: |
||
289 |
if (oflag) { |
||
290 |
/* trust command-line names */ |
||
291 |
flags &= ~(O_EXCL|O_NOFOLLOW); |
||
292 |
break; |
||
293 |
} |
||
294 |
warnc(EEXIST, "%s: %s", infile, outfile); |
||
295 |
return (1); |
||
296 |
} |
||
297 |
✗✓ | 17 |
} else if (errno != ENOENT) { |
298 |
warn("%s: %s", infile, outfile); |
||
299 |
return (1); |
||
300 |
} |
||
301 |
✓✗✗✓ |
66 |
if ((fd = open(outfile, flags, mode)) < 0 || |
302 |
33 |
(outfp = fdopen(fd, "w")) == NULL) { |
|
303 |
warn("%s: %s", infile, outfile); |
||
304 |
return (1); |
||
305 |
} |
||
306 |
} |
||
307 |
|||
308 |
✗✓ | 102 |
if (base64) |
309 |
return (base64_decode()); |
||
310 |
else |
||
311 |
102 |
return (uu_decode()); |
|
312 |
102 |
} |
|
313 |
|||
314 |
static int |
||
315 |
get_line(char *buf, size_t size) |
||
316 |
{ |
||
317 |
✓✗ | 2446 |
if (fgets(buf, size, infp) != NULL) |
318 |
1223 |
return (2); |
|
319 |
if (rflag) |
||
320 |
return (0); |
||
321 |
warnx("%s: %s: short file", infile, outfile); |
||
322 |
return (1); |
||
323 |
1223 |
} |
|
324 |
|||
325 |
static int |
||
326 |
checkend(const char *ptr, const char *end, const char *msg) |
||
327 |
{ |
||
328 |
size_t n; |
||
329 |
|||
330 |
204 |
n = strlen(end); |
|
331 |
✓✗✗✓ |
204 |
if (strncmp(ptr, end, n) != 0 || |
332 |
102 |
strspn(ptr + n, " \t\r\n") != strlen(ptr + n)) { |
|
333 |
warnx("%s: %s: %s", infile, outfile, msg); |
||
334 |
return (1); |
||
335 |
} |
||
336 |
✗✓ | 102 |
if (fclose(outfp) != 0) { |
337 |
warn("%s: %s", infile, outfile); |
||
338 |
return (1); |
||
339 |
} |
||
340 |
102 |
return (0); |
|
341 |
102 |
} |
|
342 |
|||
343 |
static int |
||
344 |
uu_decode(void) |
||
345 |
{ |
||
346 |
int i, ch; |
||
347 |
char *p; |
||
348 |
204 |
char buf[PATH_MAX]; |
|
349 |
|||
350 |
/* for each input line */ |
||
351 |
102 |
for (;;) { |
|
352 |
✗✗✓ | 1121 |
switch (get_line(buf, sizeof(buf))) { |
353 |
case 0: |
||
354 |
return (0); |
||
355 |
case 1: |
||
356 |
return (1); |
||
357 |
} |
||
358 |
|||
359 |
#define DEC(c) (((c) - ' ') & 077) /* single character decode */ |
||
360 |
#define IS_DEC(c) ( (((c) - ' ') >= 0) && (((c) - ' ') <= 077 + 1) ) |
||
361 |
|||
362 |
#define OUT_OF_RANGE do { \ |
||
363 |
warnx("%s: %s: character out of range: [%d-%d]", \ |
||
364 |
infile, outfile, 1 + ' ', 077 + ' ' + 1); \ |
||
365 |
return (1); \ |
||
366 |
} while (0) |
||
367 |
|||
368 |
/* |
||
369 |
* `i' is used to avoid writing out all the characters |
||
370 |
* at the end of the file. |
||
371 |
*/ |
||
372 |
p = buf; |
||
373 |
✓✓ | 1121 |
if ((i = DEC(*p)) <= 0) |
374 |
break; |
||
375 |
✓✓ | 31394 |
for (++p; i > 0; p += 4, i -= 3) |
376 |
✓✓ | 14678 |
if (i >= 3) { |
377 |
✓✗✓✗ ✓✗✓✗ ✗✓ |
73110 |
if (!(IS_DEC(*p) && IS_DEC(*(p + 1)) && |
378 |
✓✗✓✗ ✓✗ |
58488 |
IS_DEC(*(p + 2)) && IS_DEC(*(p + 3)))) |
379 |
OUT_OF_RANGE; |
||
380 |
|||
381 |
14622 |
ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; |
|
382 |
✓✗ | 29244 |
putc(ch, outfp); |
383 |
14622 |
ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; |
|
384 |
✓✗ | 29244 |
putc(ch, outfp); |
385 |
14622 |
ch = DEC(p[2]) << 6 | DEC(p[3]); |
|
386 |
✓✗ | 29244 |
putc(ch, outfp); |
387 |
} |
||
388 |
else { |
||
389 |
✓✗ | 56 |
if (i >= 1) { |
390 |
✓✗✓✗ ✓✗✗✓ |
224 |
if (!(IS_DEC(*p) && IS_DEC(*(p + 1)))) |
391 |
OUT_OF_RANGE; |
||
392 |
56 |
ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4; |
|
393 |
✓✗ | 112 |
putc(ch, outfp); |
394 |
} |
||
395 |
✗✓ | 56 |
if (i >= 2) { |
396 |
if (!(IS_DEC(*(p + 1)) && |
||
397 |
IS_DEC(*(p + 2)))) |
||
398 |
OUT_OF_RANGE; |
||
399 |
|||
400 |
ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2; |
||
401 |
putc(ch, outfp); |
||
402 |
} |
||
403 |
✗✓ | 56 |
if (i >= 3) { |
404 |
if (!(IS_DEC(*(p + 2)) && |
||
405 |
IS_DEC(*(p + 3)))) |
||
406 |
OUT_OF_RANGE; |
||
407 |
ch = DEC(p[2]) << 6 | DEC(p[3]); |
||
408 |
putc(ch, outfp); |
||
409 |
} |
||
410 |
} |
||
411 |
} |
||
412 |
✗✗✓ | 102 |
switch (get_line(buf, sizeof(buf))) { |
413 |
case 0: |
||
414 |
return (0); |
||
415 |
case 1: |
||
416 |
return (1); |
||
417 |
default: |
||
418 |
102 |
return (checkend(buf, "end", "no \"end\" line")); |
|
419 |
} |
||
420 |
102 |
} |
|
421 |
|||
422 |
static int |
||
423 |
base64_decode(void) |
||
424 |
{ |
||
425 |
int n; |
||
426 |
char inbuf[PATH_MAX]; |
||
427 |
unsigned char outbuf[PATH_MAX * 4]; |
||
428 |
|||
429 |
for (;;) { |
||
430 |
switch (get_line(inbuf, sizeof(inbuf))) { |
||
431 |
case 0: |
||
432 |
return (0); |
||
433 |
case 1: |
||
434 |
return (1); |
||
435 |
} |
||
436 |
n = b64_pton(inbuf, outbuf, sizeof(outbuf)); |
||
437 |
if (n < 0) |
||
438 |
break; |
||
439 |
fwrite(outbuf, 1, n, outfp); |
||
440 |
} |
||
441 |
return (checkend(inbuf, "====", |
||
442 |
"error decoding base64 input stream")); |
||
443 |
} |
||
444 |
|||
445 |
static void |
||
446 |
usage(void) |
||
447 |
{ |
||
448 |
switch (pmode) { |
||
449 |
case MODE_DECODE: |
||
450 |
(void)fprintf(stderr, |
||
451 |
"usage: uudecode [-cimprs] [file ...]\n" |
||
452 |
" uudecode [-i] -o output_file [file]\n"); |
||
453 |
break; |
||
454 |
case MODE_B64DECODE: |
||
455 |
(void)fprintf(stderr, |
||
456 |
"usage: b64decode [-ciprs] [file ...]\n" |
||
457 |
" b64decode [-i] -o output_file [file]\n"); |
||
458 |
break; |
||
459 |
} |
||
460 |
exit(1); |
||
461 |
} |
Generated by: GCOVR (Version 3.3) |