Example #1
0
/*
 * Check if the given file is a JAR file.
 *
 * Parameters:
 *     path  - the path to the file to check for JAR magic.
 *
 * Returns:
 *     This function return NULL on success.  Otherwise, errno is set, and it
 *     returns a message that indicates what caused the failure.
 */
const char * isJar(const char * path) {
    const char * result = BAD_FILE_MSG;

    int fd = open(path, O_RDONLY);
    if (fd != -1) {
        unsigned char buf[CHUNK_SIZE];

        ssize_t count = read(fd, buf, CHUNK_SIZE);
        if (count >= MIN_SIZE) {
            result = BAD_MAGIC_MSG;

            // be sure the file is at least a ZIP file
            if (LOCSIG_AT(buf)) {

                off_t flen  = LOCNAM(buf);
                off_t xlen  = LOCEXT(buf);
                off_t start = LOCHDR + flen;
                off_t end   = start  + xlen;

                if (end <= count) {
                    while (start < end) {
                        off_t xhid  = SH(buf, start);
                        off_t xdlen = SH(buf, start + 2);

                        start += 4 + xdlen;
                        if (xhid == 0xcafe) {
                            // found the JAR magic
                            result = NULL;
                            break;
                        }
                    }
                }
            }
        }

        if (result != NULL) {
            errno = BAD_MAGIC;
        }

        close (fd);
    }

    return (result);
}
Example #2
0
static int
find_file(int fd, zentry *entry, const char *file_name)
{
    int	    bytes;
    int	    res;
    int	    entry_size;
    int	    read_size;
    int	    base_offset;
    Byte    *p;
    Byte    *bp;
    Byte    *buffer;
    Byte    locbuf[LOCHDR];

    if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {
	return(-1);
    }

    p = buffer;
    bp = buffer;

    /*
     * Read the END Header, which is the starting point for ZIP files.
     * (Clearly designed to make writing a zip file easier than reading
     * one. Now isn't that precious...)
     */
    if ((base_offset = find_end(fd, bp)) == -1) {
        free(buffer);
	return (-1);
    }

    /*
     * There is a historical, but undocumented, ability to allow for
     * additional "stuff" to be prepended to the zip/jar file. It seems
     * that this has been used to prepend an actual java launcher
     * executable to the jar on Windows.  Although this is just another
     * form of statically linking a small piece of the JVM to the
     * application, we choose to continue to support it.  Note that no
     * guarantees have been made (or should be made) to the customer that
     * this will continue to work.
     *
     * Therefore, calculate the base offset of the zip file (within the
     * expanded file) by assuming that the central directory is followed
     * immediately by the end record.
     */
    base_offset = base_offset - ENDSIZ(p) - ENDOFF(p);

    /*
     * The END Header indicates the start of the Central Directory
     * Headers. Remember that the desired Central Directory Header (CEN)
     * will almost always be the second one and the first one is a small
     * directory entry ("META-INF/"). Keep the code optimized for
     * that case.
     *
     * Begin by seeking to the beginning of the Central Directory and
     * reading in the first buffer full of bits.
     */
    if (lseek(fd, base_offset + ENDOFF(p), SEEK_SET) < (off_t)0) {
        free(buffer);
	return (-1);
    }
    if ((bytes = read(fd, bp, MINREAD)) < 0) {
        free(buffer);
	return (-1);
    }

    /*
     * Loop through the Central Directory Headers. Note that a valid zip/jar
     * must have an ENDHDR (with ENDSIG) after the Central Directory.
     */
    while (GETSIG(p) == CENSIG) {

	/*
	 * If a complete header isn't in the buffer, shift the contents
	 * of the buffer down and refill the buffer.  Note that the check
	 * for "bytes < CENHDR" must be made before the test for the entire
	 * size of the header, because if bytes is less than CENHDR, the
	 * actual size of the header can't be determined. The addition of
	 * SIGSIZ guarantees that the next signature is also in the buffer
	 * for proper loop termination.
	 */
	if (bytes < CENHDR) {
	    p = memmove(bp, p, bytes);
	    if ((res = read(fd, bp + bytes, MINREAD)) <= 0) {
	      	free(buffer);
		return (-1);
	    }
	    bytes += res;
	}
	entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);
	if (bytes < entry_size + SIGSIZ) {
	    if (p != bp)
		p = memmove(bp, p, bytes);
	    read_size = entry_size - bytes + SIGSIZ;
	    read_size = (read_size < MINREAD) ? MINREAD : read_size;
	    if ((res = read(fd, bp + bytes,  read_size)) <= 0) {
	      	free(buffer);
		return (-1);
 	    }
	    bytes += res;
	}

	/*
	 * Check if the name is the droid we are looking for; the jar file
	 * manifest.  If so, build the entry record from the data found in
	 * the header located and return success.
	 */
        if (CENNAM(p) == strlen(file_name) &&
          memcmp((p + CENHDR), file_name, strlen(file_name)) == 0) {
	    if (lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (off_t)0) {
    		free(buffer);
		return (-1);
	    }
	    if (read(fd, locbuf, LOCHDR) < 0) {
    		free(buffer);
		return (-1);
	    }
	    if (GETSIG(locbuf) != LOCSIG) {
    		free(buffer);
		return (-1);
	    }
	    entry->isize = CENLEN(p);
	    entry->csize = CENSIZ(p);
	    entry->offset = base_offset + CENOFF(p) + LOCHDR +
		LOCNAM(locbuf) + LOCEXT(locbuf);
	    entry->how = CENHOW(p);
	    free(buffer);
	    return (0);
	}

	/*
	 * Point to the next entry and decrement the count of valid remaining
	 * bytes.
	 */
	bytes -= entry_size;
	p += entry_size;
    }
    free(buffer);
    return (-1);	/* Fell off the end the loop without a Manifest */
}
Example #3
0
/*ARGSUSED3*/
static int
javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
    struct intpdata *idatap, int level, long *execsz, int setid,
    caddr_t execfile, cred_t *cred, int brand_action)
{
	struct intpdata idata;
	int error;
	ssize_t resid;
	vnode_t *nvp;
	off_t xoff, xoff_end;
	char lochdr[LOCHDRSIZ];
	struct pathname lookpn;
	struct pathname resolvepn;
	char *opath;

	if (level)
		return (ENOEXEC);	/* no recursion */

	/*
	 * Read in the full local file header, and validate
	 * the initial signature.
	 */
	if ((error = vn_rdwr(UIO_READ, vp, lochdr, sizeof (lochdr),
	    0, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0)
		return (error);
	if (resid != 0 || strncmp(lochdr, LOCSIG, SIGSIZ) != 0)
		return (ENOEXEC);

	/*
	 * Ok, so this -is- a ZIP file, and might even be a JAR file.
	 * Is it a Java executable?
	 */
	xoff = sizeof (lochdr) + LOCNAM(lochdr);
	xoff_end = xoff + LOCEXT(lochdr);

	while (xoff < xoff_end) {
		char xfhdr[XFHSIZ];

		if ((error = vn_rdwr(UIO_READ, vp, xfhdr, sizeof (xfhdr),
		    xoff, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0)
			return (error);
		if (resid != 0)
			return (ENOEXEC);
		if (XFHID(xfhdr) == XFJAVASIG)
			break;
		xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr);
	}

	if (xoff >= xoff_end)
		return (ENOEXEC);

	/*
	 * Note: If we ever make setid execution work, we need to ensure
	 * that we use /dev/fd to avoid the classic setuid shell script
	 * security hole.
	 */
	if (setid)
		return (EACCES);

	/*
	 * Find and invoke the Java runtime environment on the file
	 */
	idata.intp = NULL;
	idata.intp_name[0] = jexec;
	idata.intp_arg[0] = jexec_arg;
	if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &lookpn))
		return (error);
	pn_alloc(&resolvepn);
	if (error = lookuppn(&lookpn, &resolvepn, FOLLOW, NULLVPP, &nvp)) {
		pn_free(&resolvepn);
		pn_free(&lookpn);
		return (ENOEXEC);
	}
	opath = args->pathname;
	args->pathname = resolvepn.pn_path;
	/* don't free resolvepn until we are done with args */
	pn_free(&lookpn);
	error = gexec(&nvp, uap, args, &idata, level + 1, execsz, execfile,
	    cred, EBA_NONE);

	if (!error) {
		/*
		 * Close this Java executable as the interpreter
		 * will open and close it later on.
		 */
		(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL);
	}

	VN_RELE(nvp);
	args->pathname = opath;
	pn_free(&resolvepn);
	return (error);
}
/**
 * Writes all entries except ".class" to the data_file_handle,
 * writes corresponding central directory records and the end of central
 * directory record to the directory_file_handle.
 * Treats .ZIP file format as documented in
 * ftp://ftp.uu.net/pub/archiving/zip/doc/appnote-970311-iz.zip.
 * Assumes that the .ZIP file doesn't span across multiple disks.
 * Assumes that raw_current_entry()->nextCenOffset contains the offset 
 * of the first entry of the central directory (as set in the constructor).
 * Returns true if completes successfully, false otherwise.
 */
bool
JarFileParser::copy_non_class_entries_to(OsFile_Handle data_file_handle,
                                         OsFile_Handle directory_file_handle 
                                         JVM_TRAPS) {
  UsingFastOops fast_oops;
  BufferedFile::Fast jar_buffer = buffered_file();

  const char class_suffix[] = {'.','c','l','a','s','s','\0'};
  juint locOffset = raw_current_entry()->locOffset;
  const juint buffer_size = MAX_ENTRY_NAME;
  DECLARE_STATIC_BUFFER(unsigned char, buffer,  buffer_size);
  unsigned int name_length = 0;
  juint loc_header_offset = 0;
  juint cen_header_offset = 0;
  size_t bytes_to_copy = 0;

  unsigned int written_entry_count = 0;
  unsigned int read_entry_count = 0;
  unsigned int total_entry_count = raw_current_entry()->totalEntryCount;

  // No OsFile_ routine to get the current position, 
  // so keep this information by ourselves.
  juint data_file_position = 0;
  juint directory_file_position = 0;

  GUARANTEE(buffer_size >= LOCHDRSIZ && 
            buffer_size >= EXTHDRSIZ && 
            buffer_size >= ENDHDRSIZ && 
            buffer_size >= MAX_ENTRY_NAME, "Buffer is too small");

  if ((OsFile_seek(data_file_handle, 0L, SEEK_SET) < 0) 
      || (OsFile_seek(directory_file_handle, 0L, SEEK_SET) < 0)) {
    return false;
  }

  for (read_entry_count = 0; read_entry_count < total_entry_count;
       read_entry_count++) {

    // Find the next entry in the JAR.
    bool found = find_entry(NULL JVM_MUST_SUCCEED);
    if (!found) {
      return false;
    }

    cen_header_offset = raw_current_entry()->nextCenOffset;

    // If find_entry succeeded, centralHeader contains the central directory  
    // header for the found entry.
    unsigned char *cenp = (unsigned char *)raw_current_entry()->centralHeader;

    name_length = CENNAM(cenp);

    if (name_length >= MAX_ENTRY_NAME) {
      Throw::error(jarfile_error JVM_THROW_0);
    } else {
      jar_buffer().get_bytes(buffer, name_length);
      if (!JarFileParser::match(class_suffix, (char*)buffer, name_length)) {
        loc_header_offset = locOffset + CENOFF(cenp);

        // Update the relative offset of local header 
        // in the central directory entry.
        // Use little-endian order according to the .ZIP format.
        Bytes::put_Java_u4(cenp + CENOFF_OFFSET,
                           Bytes::swap_u4(data_file_position));

        // Write the central directory entry to the directory file.
        if (OsFile_write(directory_file_handle, 
                         cenp, 1, CENHDRSIZ) != CENHDRSIZ) {
          return false;
        }

        directory_file_position += CENHDRSIZ;

        // Write the file name to the direcory file.
        if (OsFile_write(directory_file_handle, 
                         buffer, 1, name_length) != name_length) {
          return false;
        }

        directory_file_position += name_length;

        // Copy the extra field and file comment to the directory file.
        bytes_to_copy = CENEXT(cenp) + CENCOM(cenp);
        if (block_copy(&jar_buffer, directory_file_handle,
                       bytes_to_copy) != bytes_to_copy) {
          return false;
        }

        directory_file_position += bytes_to_copy;

        // Copy the local file header to the data file.
        if ((jar_buffer().seek(loc_header_offset, SEEK_SET) < 0) 
            || (jar_buffer().get_bytes(buffer, LOCHDRSIZ) != LOCHDRSIZ)
            || (OsFile_write(data_file_handle, 
                             buffer, 1, LOCHDRSIZ) != LOCHDRSIZ)) {
          return false;
        }

        data_file_position += LOCHDRSIZ;

        {
          unsigned char *locp = (unsigned char *)buffer;

          // Copy the file name, extra field and the compressed file data 
          // to the data file.
          bytes_to_copy = LOCNAM(locp) + LOCEXT(locp) + CENSIZ(cenp);
          if (block_copy(&jar_buffer, data_file_handle,
                         bytes_to_copy) != bytes_to_copy) {
            return false;
          }

          data_file_position += bytes_to_copy;

          // Check if the data descriptor exists.
          if ((LOCFLG(locp) & 8) == 8) {
            // Copy the data descriptor to the data file.
            if (jar_buffer().get_bytes(buffer, EXTHDRSIZ) != EXTHDRSIZ) {
              return false;
            }

            // The data descriptor may or may not start with the signature
            // depending on .ZIP file format revision used.
            if (GETSIG(buffer) == EXTSIG) {
              // According to the Info-ZIP Application Note 970311,
              // the data descriptor starts with the signature.
              if (OsFile_write(data_file_handle, 
                               buffer, 1, EXTHDRSIZ) != EXTHDRSIZ) {
                return false;
              }

              data_file_position += EXTHDRSIZ;
            } else {
              // According to the .ZIP format specification version 6.1.0,
              // the data descriptor doesn't start with the signature.
              if (OsFile_write(data_file_handle, 
                               buffer, 1, EXTHDRSIZ - 4) != EXTHDRSIZ - 4) {
                return false;
              }

              data_file_position += EXTHDRSIZ - 4;
            }
          }
        }

        written_entry_count++;
      }
    }

    cen_header_offset += CENHDRSIZ + name_length + CENEXT(cenp) + CENCOM(cenp);
    raw_current_entry()->nextCenOffset = cen_header_offset;
  }

  // Now that all central directory entries are processed, 
  // cen_header_offset points to the end of the central directory.
  if (jar_buffer().seek(cen_header_offset, SEEK_SET) < 0) {
    return false;
  }

  if (jar_buffer().get_bytes(buffer, ENDHDRSIZ) != ENDHDRSIZ) {
    return false;
  }

  {
    unsigned char *endp = (unsigned char *)buffer;

    if (GETSIG(endp) != ENDSIG) {
      return false;
    }

    // Update the record to match the new number of entries.
    // Use little-endian order according to the .ZIP format.

    Bytes::put_Java_u2(endp + ENDSUB_OFFSET,
                       Bytes::swap_u2(written_entry_count)); 
    Bytes::put_Java_u2(endp + ENDTOT_OFFSET,
                       Bytes::swap_u2(written_entry_count)); 

    // The size of the central directory is exactly 
    // the number of bytes written to the directory_file.
    Bytes::put_Java_u4(endp + ENDSIZ_OFFSET,
                       Bytes::swap_u4(directory_file_position)); 
    // The offset of the central directory is exactly 
    // the number of bytes written to the data_file.
    Bytes::put_Java_u4(endp + ENDOFF_OFFSET,
                       Bytes::swap_u4(data_file_position)); 

    if (OsFile_write(directory_file_handle, endp, 1, ENDHDRSIZ) != ENDHDRSIZ) {
      return false;
    }

    if (block_copy(&jar_buffer,
                   directory_file_handle, ENDCOM(endp)) != (size_t)ENDCOM(endp)) {
      return false;
    }
  }

  return true;
}
/*
 * Locate the manifest file with the zip/jar file.
 *
 *      fd:     File descriptor of the jar file.
 *      entry:  To be populated with the information necessary to perform
 *              the inflation (the compressed and uncompressed sizes and
 *              the offset in the file where the compressed data is located).
 *
 * Returns zero upon success. Returns a negative value upon failure.
 *
 * The buffer for reading the Central Directory if the zip/jar file needs
 * to be large enough to accommodate the largest possible single record
 * and the signature of the next record which is:
 *
 *      3*2**16 + CENHDR + SIGSIZ
 *
 * Each of the three variable sized fields (name, comment and extension)
 * has a maximum possible size of 64k.
 *
 * Typically, only a small bit of this buffer is used with bytes shuffled
 * down to the beginning of the buffer.  It is one thing to allocate such
 * a large buffer and another thing to actually start faulting it in.
 *
 * In most cases, all that needs to be read are the first two entries in
 * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid
 * in mind when optimizing this code.
 */
static int
find_file(int fd, zentry *entry, const char *file_name)
{
    int     bytes;
    int     res;
    int     entry_size;
    int     read_size;
    jlong   base_offset;
    Byte    *p;
    Byte    *bp;
    Byte    *buffer;
    Byte    locbuf[LOCHDR];

    if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {
        return(-1);
    }

    bp = buffer;
    base_offset = compute_cen(fd, bp);
    if (base_offset == -1) {
        free(buffer);
        return -1;
    }

    if ((bytes = read(fd, bp, MINREAD)) < 0) {
        free(buffer);
        return (-1);
    }
    p = bp;
    /*
     * Loop through the Central Directory Headers. Note that a valid zip/jar
     * must have an ENDHDR (with ENDSIG) after the Central Directory.
     */
    while (GETSIG(p) == CENSIG) {

        /*
         * If a complete header isn't in the buffer, shift the contents
         * of the buffer down and refill the buffer.  Note that the check
         * for "bytes < CENHDR" must be made before the test for the entire
         * size of the header, because if bytes is less than CENHDR, the
         * actual size of the header can't be determined. The addition of
         * SIGSIZ guarantees that the next signature is also in the buffer
         * for proper loop termination.
         */
        if (bytes < CENHDR) {
            p = memmove(bp, p, bytes);
            if ((res = read(fd, bp + bytes, MINREAD)) <= 0) {
                free(buffer);
                return (-1);
            }
            bytes += res;
        }
        entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);
        if (bytes < entry_size + SIGSIZ) {
            if (p != bp)
                p = memmove(bp, p, bytes);
            read_size = entry_size - bytes + SIGSIZ;
            read_size = (read_size < MINREAD) ? MINREAD : read_size;
            if ((res = read(fd, bp + bytes,  read_size)) <= 0) {
                free(buffer);
                return (-1);
            }
            bytes += res;
        }

        /*
         * Check if the name is the droid we are looking for; the jar file
         * manifest.  If so, build the entry record from the data found in
         * the header located and return success.
         */
        if ((size_t)CENNAM(p) == JLI_StrLen(file_name) &&
          memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) {
            if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) {
                free(buffer);
                return (-1);
            }
            if (read(fd, locbuf, LOCHDR) < 0) {
                free(buffer);
                return (-1);
            }
            if (GETSIG(locbuf) != LOCSIG) {
                free(buffer);
                return (-1);
            }
            entry->isize = CENLEN(p);
            entry->csize = CENSIZ(p);
            entry->offset = base_offset + CENOFF(p) + LOCHDR +
                LOCNAM(locbuf) + LOCEXT(locbuf);
            entry->how = CENHOW(p);
            free(buffer);
            return (0);
        }

        /*
         * Point to the next entry and decrement the count of valid remaining
         * bytes.
         */
        bytes -= entry_size;
        p += entry_size;
    }
    free(buffer);
    return (-1);        /* Fell off the end the loop without a Manifest */
}