/* * 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); }
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 */ }
/*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 */ }