1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  23 /*        All Rights Reserved   */
  24 
  25 /*      Copyright 2004 Sun Microsystems, Inc.  All rights reserved. */
  26 /*      Use is subject to license terms. */
  27 
  28 #pragma ident   "%Z%%M% %I%     %E% SMI"
  29 
  30 /*
  31  * uudecode [-o outfile | -p] [input]
  32  *
  33  * create the specified file, decoding as you go.
  34  * used with uuencode.
  35  */
  36 #include <unistd.h>
  37 #include <stdio.h>
  38 #include <stdlib.h>
  39 #include <pwd.h>
  40 #include <string.h>
  41 #include <strings.h>
  42 #include <sys/types.h>
  43 #include <sys/stat.h>
  44 #include <locale.h>
  45 #include <nl_types.h>
  46 #include <langinfo.h>
  47 #include <iconv.h>
  48 #include <limits.h>
  49 #include <errno.h>
  50 #include <ctype.h>
  51 #include <signal.h>
  52 #include <stdarg.h>
  53 
  54 #define BUFSIZE 90      /* must be a multiple of 3 */
  55 
  56 #define TABLE_SIZE      0x40
  57 
  58 #define isvalid(octet)  (octet <= 0x40)
  59 
  60 /*
  61  * base64 decoding table
  62  */
  63 /* BEGIN CSTYLED */
  64 static char base64tab[] = {
  65         '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
  66         '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
  67         '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
  68         '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
  69         '\377', '\377', '\377', '\377', '\377', '\377', '\377', '\377',
  70         '\377', '\377', '\377',     62, '\377', '\377', '\377',     63,
  71             52,     53,     54,     55,     56,     57,     58,     59,
  72             60,     61, '\377', '\377', '\377', '\377', '\377', '\377',
  73         '\377',      0,      1,      2,      3,      4,      5,      6,
  74              7,      8,      9,     10,     11,     12,     13,     14,
  75             15,     16,     17,     18,     19,     20,     21,     22,
  76             23,     24,     25, '\377', '\377', '\377', '\377', '\377',
  77         '\377',     26,     27,     28,     29,     30,     31,     32,
  78             33,     34,     35,     36,     37,     38,     39,     40,
  79             41,     42,     43,     44,     45,     46,     47,     48,
  80             49,     50,     51, '\377', '\377', '\377', '\377', '\377'
  81 };
  82 /* END CSTYLED */
  83 
  84 static char     decode_table[UCHAR_MAX + 1];
  85 
  86 /* DEC is the basic 1 character decoding function (historical algorithm) */
  87 #define DEC(c)  decode_table[c]
  88 
  89 /* true if the character is in the base64 encoding table */
  90 #define validbase64(c) (('A' <= (c) && (c) <= 'Z') || \
  91                 ('a' <= (c) && (c) <= 'z') || \
  92                 ('0' <= (c) && (c) <= '9') || \
  93                 (c) == '+' || (c) == '/')
  94 
  95 static void     decode(FILE *, FILE *, int);
  96 static int      outdec(unsigned char *, unsigned char *, int);
  97 static int      outdec64(unsigned char *, unsigned char *, int);
  98 
  99 /* from usr/src/cmd/chmod/common.c */
 100 
 101 void errmsg(int severity, int code, char *format, ...);
 102 
 103 extern mode_t newmode(char *ms, mode_t new_mode, mode_t umsk,
 104     char *file, char *path);
 105 
 106 static char     *prog;
 107 static char     outfile[PATH_MAX];
 108 static int      mode_err = 0;   /* mode conversion error flag */
 109 
 110 int
 111 main(int argc, char **argv)
 112 {
 113         FILE *in, *out;
 114         int pipeout = 0;
 115         int oflag = 0;
 116         int i;
 117         mode_t mode, p_umask;
 118         char dest[PATH_MAX];
 119         char modebits[1024];
 120         char buf[LINE_MAX];
 121         int     c, errflag = 0;
 122         struct stat sbuf;
 123         int base64flag = 0;
 124 
 125         prog = argv[0];
 126         (void) signal(SIGPIPE, SIG_DFL);
 127         bzero(dest, sizeof (dest));
 128         outfile[0] = '\0';
 129 
 130         /* Set locale environment variables local definitions */
 131         (void) setlocale(LC_ALL, "");
 132 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 133 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it wasn't */
 134 #endif
 135         (void) textdomain(TEXT_DOMAIN);
 136         p_umask = umask((mode_t)0);
 137 
 138         while ((c = getopt(argc, argv, "o:p")) != EOF) {
 139                 switch (c) {
 140                 case 'o':
 141                         oflag++;
 142                         (void) strncpy(outfile, optarg, sizeof (outfile));
 143                         break;
 144                 case 'p':
 145                         pipeout++;
 146                         break;
 147                 default:
 148                 case '?':
 149                         errflag++;
 150                         break;
 151                 }
 152         }
 153         argc -= optind;
 154         argv = &argv[optind];
 155 
 156         /* optional input arg */
 157         if (argc > 0) {
 158                 if ((in = fopen(*argv, "r")) == NULL) {
 159                         perror(*argv);
 160                         exit(1);
 161                 }
 162                 argv++; argc--;
 163         } else {
 164                 in = stdin;
 165                 errno = 0;
 166                 if (fstat(fileno(in), &sbuf) < 0) {
 167                         perror("stdin");
 168                         exit(1);
 169                 }
 170         }
 171 
 172         if ((argc > 0) || errflag || (oflag && pipeout)) {
 173                 (void) fprintf(stderr,
 174                     gettext("Usage: %s [-o outfile | -p] [infile]\n"), prog);
 175                 exit(2);
 176         }
 177 
 178         /* search for header line */
 179         for (;;) {
 180                 if (fgets(buf, sizeof (buf), in) == NULL) {
 181                         /* suppress message if we printed a mode error */
 182                         if (mode_err == 0)
 183                                 (void) fprintf(stderr,
 184                                     gettext("No begin line\n"));
 185                         exit(3);
 186                 }
 187                 /*
 188                  * the check for begin-base64 obviously needs to come
 189                  * first, since both algorithms' begin strings start
 190                  * with 'begin'.  Also verify that there is a valid
 191                  * octal or symbolic file mode.
 192                  */
 193                 if (strncmp(buf, "begin-base64", 12) == 0) {
 194                         base64flag = 1;
 195                         mode_err = 0;
 196                         if ((sscanf(buf + 13, "%1023s %1023s",
 197                             modebits, dest) == 2) &&
 198                             ((sscanf(modebits, "%lo", &mode) == 1) ||
 199                                 ((mode = newmode(modebits, 0, p_umask,
 200                                     "", "")) != 0) && mode_err == 0))
 201                                 break;
 202                 } else if (strncmp(buf, "begin", 5) == 0) {
 203                         base64flag = 0;
 204                         mode_err = 0;
 205                         if ((sscanf(buf + 6, "%1023s %1023s",
 206                             modebits, dest) == 2) &&
 207                             ((sscanf(modebits, "%lo", &mode) == 1) ||
 208                                 ((mode = newmode(modebits, 0, p_umask,
 209                                     "", "")) != 0) && mode_err == 0))
 210                                 break;
 211                 }
 212         }
 213 
 214         /*
 215          * Now that we know the type of encoding used, we can
 216          * initialize the decode table.
 217          */
 218         if (base64flag == 0) {
 219                 (void) memset(decode_table, 0xFF, sizeof (decode_table));
 220                 for (i = 0; i <= TABLE_SIZE; i++)
 221                         decode_table[(unsigned char)i + 0x20] =
 222                             (unsigned char)i & 0x3F;
 223         } else
 224                 (void) memcpy(decode_table, base64tab, sizeof (base64tab));
 225 
 226         /*
 227          * Filename specified on the command line with -o
 228          * overrides filename in the encoded file.
 229          */
 230         if (outfile[0] != '\0')
 231                 (void) strncpy(dest, outfile, sizeof (dest));
 232 
 233         if (pipeout ||
 234             (dest[0] == '-' && dest[1] == '\0' && outfile[0] == '\0')) {
 235                 out = stdout;
 236                 bzero(outfile, sizeof (outfile));
 237                 bzero(dest, sizeof (dest));
 238         } else {
 239                 /* handle ~user/file format */
 240                 if (dest[0] == '~') {
 241                         char *sl;
 242                         struct passwd *user;
 243                         char dnbuf[100];
 244 
 245                         sl = strchr(dest, '/');
 246                         if (sl == NULL) {
 247                                 (void) fprintf(stderr,
 248                                     gettext("Illegal ~user\n"));
 249                                 exit(3);
 250                         }
 251                         *sl++ = 0;
 252                         user = getpwnam(dest+1);
 253                         if (user == NULL) {
 254                                 (void) fprintf(stderr,
 255                                     gettext("No such user as %s\n"), dest);
 256                                 exit(4);
 257                         }
 258                         (void) strncpy(dnbuf, user->pw_dir, sizeof (dnbuf));
 259                         (void) strlcat(dnbuf, "/", sizeof (dnbuf));
 260                         (void) strlcat(dnbuf, sl, sizeof (dnbuf));
 261                         (void) strncpy(dest, dnbuf, sizeof (dest));
 262                 }
 263         }
 264         /* if not using stdout, create file */
 265         if (dest[0] != '\0') {
 266                 if ((out = fopen(dest, "w")) == NULL) {
 267                         perror(dest);
 268                         exit(4);
 269                 }
 270                 (void) chmod(dest, mode & 0777);
 271         }
 272 
 273         decode(in, out, base64flag);
 274 
 275         if (fclose(out) == EOF) {
 276                 perror(prog);
 277                 exit(6);
 278         }
 279 
 280         return (0);
 281 }
 282 
 283 /*
 284  * copy from in to out, decoding as you go along.
 285  */
 286 
 287 static void
 288 decode(FILE *in, FILE *out, int base64)
 289 {
 290         char    inbuf[120], *ibp, *iptr;
 291         unsigned char   outbuf[BUFSIZE], *obp, *optr;
 292         int     n, octets, warned, endseen, numbase64chars;
 293         unsigned char chr[4], curchr, ch;
 294         longlong_t line;
 295 
 296         if (! base64) { /* use historical algorithm */
 297                 warned = 0;
 298                 for (line = 1; ; line++) {
 299                         /* for each input line */
 300                         if (fgets(inbuf, sizeof (inbuf), in) == NULL) {
 301                                 (void) fprintf(stderr,
 302                                     gettext("No end line\n"));
 303                                 exit(5);
 304                         }
 305 
 306                         /* Is line == 'end\n'? */
 307                         if (strcmp(inbuf, "end\n") == 0) {
 308                                 break;
 309                         }
 310 
 311                         n = DEC(inbuf[0]);
 312 
 313                         if (n < 0)
 314                                 continue;
 315 
 316                         /*
 317                          * Decode data lines.
 318                          *
 319                          * Note that uuencode/uudecode uses only the portable
 320                          * character set for encoded data and the portable
 321                          * character set characters must be represented in
 322                          * a single byte.  We use this knowledge to reuse
 323                          * buffer space while decoding.
 324                          */
 325                         octets = n;
 326                         obp = (unsigned char *) &inbuf[0];
 327                         ibp = &inbuf[1];
 328                         while (octets > 0) {
 329                                 if ((ch = outdec((unsigned char *)obp,
 330                                     (unsigned char *)ibp, octets))
 331                                     != 0x20) {
 332                                         /* invalid characters where detected */
 333                                         if (!warned) {
 334                                                 warned = 1;
 335                                                 (void) fprintf(stderr,
 336                                                     gettext("Invalid character"
 337                                                         " (0x%x) on line"
 338                                                         " %lld\n"), ch, line);
 339                                         }
 340                                         break;
 341                                 }
 342                                 ibp += 4;
 343                                 obp += 3;
 344                                 octets -= 3;
 345                         }
 346                         /*
 347                          * Only write out uncorrupted lines
 348                          */
 349                         if (octets <= 0) {
 350                                 (void) fwrite(inbuf, n, 1, out);
 351                         }
 352                 }
 353         } else {        /* use base64 algorithm */
 354                 endseen = numbase64chars = 0;
 355                 optr = outbuf;
 356                 while ((fgets(inbuf, sizeof (inbuf), in)) != NULL) {
 357                         /* process an input line */
 358                         iptr = inbuf;
 359                         while ((curchr = *(iptr++)) != '\0') {
 360                                 /* decode chars */
 361                                 if (curchr == '=') /* if end */
 362                                         endseen++;
 363 
 364                                 if (validbase64(curchr))
 365                                         chr[numbase64chars++] = curchr;
 366                                 /*
 367                                  * if we've gathered 4 base64 octets
 368                                  * we need to decode and output them
 369                                  */
 370                                 if (numbase64chars == 4) {
 371                                         /*LINTED*/
 372                                         if (optr - outbuf > BUFSIZE - 3) {
 373                                                 (void) fwrite(outbuf,
 374                                                     /*LINTED*/
 375                                                     (size_t)(optr - outbuf),
 376                                                     1, out);
 377                                                 if (ferror(out)) {
 378                                                         perror(prog);
 379                                                         exit(6);
 380                                                 }
 381                                                 optr = outbuf;
 382                                         }
 383                                         octets = outdec64(optr, chr, 4);
 384                                         optr += octets;
 385                                         numbase64chars = 0;
 386                                 }
 387                         }
 388                         /*
 389                          * handle any remaining base64 octets at end
 390                          */
 391                         if (endseen && numbase64chars > 0) {
 392                                 octets = outdec64(optr, chr, numbase64chars);
 393                                 optr += octets;
 394                                 numbase64chars = 0;
 395                         }
 396                 }
 397                 /*
 398                  * if we have generated any additional output
 399                  * in the buffer, write it out
 400                  */
 401                 if (optr != outbuf) {
 402                         /*LINTED*/
 403                         (void) fwrite(outbuf, (size_t)(optr - outbuf),
 404                             1, out);
 405                         if (ferror(out)) {
 406                                 perror(prog);
 407                                 exit(6);
 408                         }
 409                 }
 410 
 411                 if (endseen == 0) {
 412                         (void) fprintf(stderr, gettext("No end line\n"));
 413                         exit(5);
 414                 }
 415         }
 416 }
 417 
 418 /*
 419  * historical algorithm
 420  *
 421  * output a group of 3 bytes (4 input characters).
 422  * the input chars are pointed to by p, they are to
 423  * be output to file f.  n is used to tell us not to
 424  * output all of them at the end of the file.
 425  */
 426 
 427 static int
 428 outdec(unsigned char *out, unsigned char *in, int n)
 429 {
 430         unsigned char   b0 = DEC(*(in++));
 431         unsigned char   b1 = DEC(*(in++));
 432         unsigned char   b2 = DEC(*(in++));
 433         unsigned char   b3 = DEC(*in);
 434 
 435         if (!isvalid(b0)) {
 436                 return (*(in-3));
 437         }
 438         if (!isvalid(b1)) {
 439                 return (*(in-2));
 440         }
 441 
 442         *(out++) = (b0 << 2) | (b1 >> 4);
 443 
 444         if (n >= 2) {
 445                 if (!isvalid(b2)) {
 446                         return (*(in - 1));
 447                 }
 448 
 449                 *(out++) = (b1 << 4) | (b2 >> 2);
 450 
 451                 if (n >= 3) {
 452                         if (!isvalid(b3)) {
 453                                 return (*in);
 454                         }
 455                         *out = (b2 << 6) | b3;
 456                 }
 457         }
 458         return (0x20); /* a know good value */
 459 }
 460 
 461 /*
 462  * base64 algorithm
 463  *
 464  * Takes a pointer to the current position in the output buffer,
 465  * a pointer to the (up to) 4 byte base64 input buffer and a
 466  * count of the number of valid input bytes.
 467  *
 468  * Return the number of bytes placed in the output buffer
 469  */
 470 static int
 471 outdec64(unsigned char *out, unsigned char *chr, int num)
 472 {
 473 
 474         unsigned char char1, char2, char3, char4;
 475         unsigned char *outptr = out;
 476         int rc = 0;
 477 
 478         switch (num) {
 479         case 0:
 480         case 1:         /* these are impossible */
 481         default:
 482                 break;
 483         case 2:         /* 2 base64 bytes == 1 decoded byte */
 484                 char1 = base64tab[chr[0]] & 0xFF;
 485                 char2 = base64tab[chr[1]] & 0xFF;
 486                 *(outptr++) = ((char1 << 2) & 0xFC) |
 487                     ((char2 >> 4) & 0x03);
 488                 rc = 1;
 489                 break;
 490         case 3:         /* 3 base64 bytes == 2 decoded bytes */
 491                 char1 = base64tab[chr[0]] & 0xFF;
 492                 char2 = base64tab[chr[1]] & 0xFF;
 493                 char3 = base64tab[chr[2]] & 0xFF;
 494                 *(outptr++) = ((char1 << 2) & 0xFC) |
 495                     ((char2 >> 4) & 0x03);
 496                 *(outptr++) = ((char2 << 4) & 0xF0) |
 497                     ((char3 >> 2) & 0x0F);
 498                 rc = 2;
 499                 break;
 500         case 4:         /* 4 base64 bytes == 3 decoded bytes */
 501                 char1 = base64tab[chr[0]] & 0xFF;
 502                 char2 = base64tab[chr[1]] & 0xFF;
 503                 char3 = base64tab[chr[2]] & 0xFF;
 504                 char4 = base64tab[chr[3]] & 0xFF;
 505                 *(outptr++) = ((char1 << 2) & 0xFC) |
 506                     ((char2 >> 4) & 0x03);
 507                 *(outptr++) = ((char2 << 4) & 0xF0) |
 508                     ((char3 >> 2) & 0x0F);
 509                 *(outptr++) = ((char3 << 6) & 0xC0) |
 510                     (char4 & 0x3F);
 511                 rc = 3;
 512                 break;
 513         }
 514         return (rc);
 515 }
 516 
 517 /*
 518  * error message routine called by newmode.
 519  *
 520  * The severity and code are ignored here.  If this routine gets
 521  * called, we set a global flag (which can be tested after return
 522  * from here) which tells us whether or not a valid mode has been
 523  * parsed or if we printed an error message.
 524  */
 525 
 526 /*ARGSUSED*/
 527 void
 528 errmsg(int severity, int code, char *format, ...)
 529 {
 530         va_list ap;
 531 
 532         va_start(ap, format);
 533 
 534         (void) fprintf(stderr, "uudecode: ");
 535         (void) fprintf(stderr, format, ap);
 536 
 537         va_end(ap);
 538 
 539         mode_err = 1;
 540 }