Merge
This commit is contained in:
commit
9a130fd30d
@ -1174,6 +1174,12 @@ public final class System {
|
||||
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
|
||||
Shutdown.add(slot, registerShutdownInProgress, hook);
|
||||
}
|
||||
public int getStackTraceDepth(Throwable t) {
|
||||
return t.getStackTraceDepth();
|
||||
}
|
||||
public StackTraceElement getStackTraceElement(Throwable t, int i) {
|
||||
return t.getStackTraceElement(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -645,17 +645,21 @@ public class Throwable implements Serializable {
|
||||
/**
|
||||
* Returns the number of elements in the stack trace (or 0 if the stack
|
||||
* trace is unavailable).
|
||||
*
|
||||
* package-protection for use by SharedSecrets.
|
||||
*/
|
||||
private native int getStackTraceDepth();
|
||||
native int getStackTraceDepth();
|
||||
|
||||
/**
|
||||
* Returns the specified element of the stack trace.
|
||||
*
|
||||
* package-protection for use by SharedSecrets.
|
||||
*
|
||||
* @param index index of the element to return.
|
||||
* @throws IndexOutOfBoundsException if <tt>index < 0 ||
|
||||
* index >= getStackTraceDepth() </tt>
|
||||
*/
|
||||
private native StackTraceElement getStackTraceElement(int index);
|
||||
native StackTraceElement getStackTraceElement(int index);
|
||||
|
||||
private synchronized void writeObject(java.io.ObjectOutputStream s)
|
||||
throws IOException
|
||||
|
@ -29,6 +29,9 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.io.*;
|
||||
|
||||
import sun.misc.JavaLangAccess;
|
||||
import sun.misc.SharedSecrets;
|
||||
|
||||
/**
|
||||
* LogRecord objects are used to pass logging requests between
|
||||
* the logging framework and individual log Handlers.
|
||||
@ -522,29 +525,31 @@ public class LogRecord implements java.io.Serializable {
|
||||
// Private method to infer the caller's class and method names
|
||||
private void inferCaller() {
|
||||
needToInferCaller = false;
|
||||
// Get the stack trace.
|
||||
StackTraceElement stack[] = (new Throwable()).getStackTrace();
|
||||
// First, search back to a method in the Logger class.
|
||||
int ix = 0;
|
||||
while (ix < stack.length) {
|
||||
StackTraceElement frame = stack[ix];
|
||||
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
|
||||
Throwable throwable = new Throwable();
|
||||
int depth = access.getStackTraceDepth(throwable);
|
||||
|
||||
String logClassName = "java.util.logging.Logger";
|
||||
boolean lookingForLogger = true;
|
||||
for (int ix = 0; ix < depth; ix++) {
|
||||
// Calling getStackTraceElement directly prevents the VM
|
||||
// from paying the cost of building the entire stack frame.
|
||||
StackTraceElement frame =
|
||||
access.getStackTraceElement(throwable, ix);
|
||||
String cname = frame.getClassName();
|
||||
if (cname.equals("java.util.logging.Logger")) {
|
||||
break;
|
||||
if (lookingForLogger) {
|
||||
// Skip all frames until we have found the first logger frame.
|
||||
if (cname.equals(logClassName)) {
|
||||
lookingForLogger = false;
|
||||
}
|
||||
} else {
|
||||
if (!cname.equals(logClassName)) {
|
||||
// We've found the relevant frame.
|
||||
setSourceClassName(cname);
|
||||
setSourceMethodName(frame.getMethodName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
ix++;
|
||||
}
|
||||
// Now search for the first frame before the "Logger" class.
|
||||
while (ix < stack.length) {
|
||||
StackTraceElement frame = stack[ix];
|
||||
String cname = frame.getClassName();
|
||||
if (!cname.equals("java.util.logging.Logger")) {
|
||||
// We've found the relevant frame.
|
||||
setSourceClassName(cname);
|
||||
setSourceMethodName(frame.getMethodName());
|
||||
return;
|
||||
}
|
||||
ix++;
|
||||
}
|
||||
// We haven't found a suitable frame, so just punt. This is
|
||||
// OK as we are only committed to making a "best effort" here.
|
||||
|
@ -73,4 +73,14 @@ public interface JavaLangAccess {
|
||||
* the slot is not valid to register.
|
||||
*/
|
||||
void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
|
||||
|
||||
/**
|
||||
* Returns the number of stack frames represented by the given throwable.
|
||||
*/
|
||||
int getStackTraceDepth(Throwable t);
|
||||
|
||||
/**
|
||||
* Returns the ith StackTraceElement for the given throwable.
|
||||
*/
|
||||
StackTraceElement getStackTraceElement(Throwable t, int i);
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ package sun.security.x509;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import sun.security.util.*;
|
||||
|
@ -140,7 +140,11 @@ public class InvalidityDateExtension extends Extension
|
||||
*/
|
||||
public Object get(String name) throws IOException {
|
||||
if (name.equalsIgnoreCase(DATE)) {
|
||||
return date;
|
||||
if (date == null) {
|
||||
return null;
|
||||
} else {
|
||||
return (new Date(date.getTime())); // clone
|
||||
}
|
||||
} else {
|
||||
throw new IOException
|
||||
("Name not supported by InvalidityDateExtension");
|
||||
|
@ -49,6 +49,18 @@
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef USE_CLONE
|
||||
#ifdef __linux__
|
||||
#define USE_CLONE 1
|
||||
#else
|
||||
#define USE_CLONE 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if USE_CLONE
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#ifndef STDIN_FILENO
|
||||
#define STDIN_FILENO 0
|
||||
#endif
|
||||
@ -376,70 +388,61 @@ debugPrint(char *format, ...)
|
||||
}
|
||||
#endif /* DEBUG_PROCESS */
|
||||
|
||||
/* Version of execvpe when child's PATH differs from parent's */
|
||||
static int
|
||||
execvp_usingParentPath(const char *file, const char *const argv[])
|
||||
/**
|
||||
* Exec FILE as a traditional Bourne shell script (i.e. one without #!).
|
||||
* If we could do it over again, we would probably not support such an ancient
|
||||
* misfeature, but compatibility wins over sanity. The original support for
|
||||
* this was imported accidentally from execvp().
|
||||
*/
|
||||
static void
|
||||
execve_as_traditional_shell_script(const char *file,
|
||||
const char *argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
char expanded_file[PATH_MAX];
|
||||
int filelen = strlen(file);
|
||||
int sticky_errno = 0;
|
||||
const char * const * dirs;
|
||||
/* Search parent's PATH */
|
||||
for (dirs = parentPathv; *dirs; dirs++) {
|
||||
const char * dir = *dirs;
|
||||
int dirlen = strlen(dir);
|
||||
if (filelen + dirlen + 1 >= PATH_MAX) {
|
||||
/* Resist the urge to remove this limit;
|
||||
* calling malloc after fork is unsafe. */
|
||||
errno = ENAMETOOLONG;
|
||||
continue;
|
||||
}
|
||||
strcpy(expanded_file, dir);
|
||||
strcpy(expanded_file + dirlen, file);
|
||||
execvp(expanded_file, (char **) argv);
|
||||
/* There are 3 responses to various classes of errno:
|
||||
* return immediately, continue (especially for ENOENT),
|
||||
* or continue with "sticky" errno.
|
||||
*
|
||||
* From exec(3):
|
||||
*
|
||||
* If permission is denied for a file (the attempted
|
||||
* execve returned EACCES), these functions will continue
|
||||
* searching the rest of the search path. If no other
|
||||
* file is found, however, they will return with the
|
||||
* global variable errno set to EACCES.
|
||||
*/
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
sticky_errno = errno;
|
||||
/* FALLTHRU */
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
#ifdef ELOOP
|
||||
case ELOOP:
|
||||
#endif
|
||||
#ifdef ESTALE
|
||||
case ESTALE:
|
||||
#endif
|
||||
#ifdef ENODEV
|
||||
case ENODEV:
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
case ETIMEDOUT:
|
||||
#endif
|
||||
break; /* Try other directories in PATH */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (sticky_errno != 0)
|
||||
errno = sticky_errno;
|
||||
return -1;
|
||||
/* Use the extra word of space provided for us in argv by caller. */
|
||||
const char *argv0 = argv[0];
|
||||
const char *const *end = argv;
|
||||
while (*end != NULL)
|
||||
++end;
|
||||
memmove(argv+2, argv+1, (end-argv) * sizeof (*end));
|
||||
argv[0] = "/bin/sh";
|
||||
argv[1] = file;
|
||||
execve(argv[0], (char **) argv, (char **) envp);
|
||||
/* Can't even exec /bin/sh? Big trouble, but let's soldier on... */
|
||||
memmove(argv+1, argv+2, (end-argv) * sizeof (*end));
|
||||
argv[0] = argv0;
|
||||
}
|
||||
|
||||
/* execvpe should have been included in the Unix standards. */
|
||||
static int
|
||||
execvpe(const char *file, const char *const argv[], const char *const envp[])
|
||||
/**
|
||||
* Like execve(2), except that in case of ENOEXEC, FILE is assumed to
|
||||
* be a shell script and the system default shell is invoked to run it.
|
||||
*/
|
||||
static void
|
||||
execve_with_shell_fallback(const char *file,
|
||||
const char *argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
#if USE_CLONE
|
||||
execve(file, (char **) argv, (char **) envp);
|
||||
if (errno == ENOEXEC)
|
||||
execve_as_traditional_shell_script(file, argv, envp);
|
||||
#else
|
||||
/* Our address space is unshared, so can mutate environ. */
|
||||
extern char **environ;
|
||||
environ = (char **) envp;
|
||||
execvp(file, (char **) argv);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* execvpe should have been included in the Unix standards.
|
||||
* execvpe is identical to execvp, except that the child environment is
|
||||
* specified via the 3rd argument instead of being inherited from environ.
|
||||
*/
|
||||
static void
|
||||
execvpe(const char *file,
|
||||
const char *argv[],
|
||||
const char *const envp[])
|
||||
{
|
||||
/* This is one of the rare times it's more portable to declare an
|
||||
* external symbol explicitly, rather than via a system header.
|
||||
@ -454,28 +457,73 @@ execvpe(const char *file, const char *const argv[], const char *const envp[])
|
||||
*/
|
||||
extern char **environ;
|
||||
|
||||
if (envp != NULL)
|
||||
environ = (char **) envp;
|
||||
if (envp == NULL || (char **) envp == environ) {
|
||||
execvp(file, (char **) argv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (/* Parent and child environment the same? Use child PATH. */
|
||||
(envp == NULL)
|
||||
if (*file == '\0') {
|
||||
errno = ENOENT;
|
||||
return;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/009695399/functions/exec.html
|
||||
* "If the file argument contains a slash character, it is used as
|
||||
* the pathname for this file. Otherwise, the path prefix for this
|
||||
* file is obtained by a search of the directories passed in the
|
||||
* PATH environment variable" */
|
||||
|| (strchr(file, '/') != NULL)
|
||||
|
||||
/* Parent and child PATH the same? Use child PATH. */
|
||||
|| (strcmp(parentPath, effectivePath()) == 0)
|
||||
|
||||
/* We want ENOENT, not EACCES, for zero-length program names. */
|
||||
|| (*file == '\0'))
|
||||
|
||||
return execvp(file, (char **) argv);
|
||||
else
|
||||
return execvp_usingParentPath(file, argv);
|
||||
if (strchr(file, '/') != NULL) {
|
||||
execve_with_shell_fallback(file, argv, envp);
|
||||
} else {
|
||||
/* We must search PATH (parent's, not child's) */
|
||||
char expanded_file[PATH_MAX];
|
||||
int filelen = strlen(file);
|
||||
int sticky_errno = 0;
|
||||
const char * const * dirs;
|
||||
for (dirs = parentPathv; *dirs; dirs++) {
|
||||
const char * dir = *dirs;
|
||||
int dirlen = strlen(dir);
|
||||
if (filelen + dirlen + 1 >= PATH_MAX) {
|
||||
errno = ENAMETOOLONG;
|
||||
continue;
|
||||
}
|
||||
memcpy(expanded_file, dir, dirlen);
|
||||
memcpy(expanded_file + dirlen, file, filelen);
|
||||
expanded_file[dirlen + filelen] = '\0';
|
||||
execve_with_shell_fallback(expanded_file, argv, envp);
|
||||
/* There are 3 responses to various classes of errno:
|
||||
* return immediately, continue (especially for ENOENT),
|
||||
* or continue with "sticky" errno.
|
||||
*
|
||||
* From exec(3):
|
||||
*
|
||||
* If permission is denied for a file (the attempted
|
||||
* execve returned EACCES), these functions will continue
|
||||
* searching the rest of the search path. If no other
|
||||
* file is found, however, they will return with the
|
||||
* global variable errno set to EACCES.
|
||||
*/
|
||||
switch (errno) {
|
||||
case EACCES:
|
||||
sticky_errno = errno;
|
||||
/* FALLTHRU */
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
#ifdef ELOOP
|
||||
case ELOOP:
|
||||
#endif
|
||||
#ifdef ESTALE
|
||||
case ESTALE:
|
||||
#endif
|
||||
#ifdef ENODEV
|
||||
case ENODEV:
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
case ETIMEDOUT:
|
||||
#endif
|
||||
break; /* Try other directories in PATH */
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (sticky_errno != 0)
|
||||
errno = sticky_errno;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -516,10 +564,95 @@ readFully(int fd, void *buf, size_t nbyte)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __solaris__
|
||||
#undef fork1
|
||||
#define fork1() fork()
|
||||
#endif
|
||||
typedef struct _ChildStuff
|
||||
{
|
||||
int in[2];
|
||||
int out[2];
|
||||
int err[2];
|
||||
int fail[2];
|
||||
int fds[3];
|
||||
const char **argv;
|
||||
const char **envv;
|
||||
const char *pdir;
|
||||
jboolean redirectErrorStream;
|
||||
} ChildStuff;
|
||||
|
||||
static void
|
||||
copyPipe(int from[2], int to[2])
|
||||
{
|
||||
to[0] = from[0];
|
||||
to[1] = from[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Child process after a successful fork() or clone().
|
||||
* This function must not return, and must be prepared for either all
|
||||
* of its address space to be shared with its parent, or to be a copy.
|
||||
* It must not modify global variables such as "environ".
|
||||
*/
|
||||
static int
|
||||
childProcess(void *arg)
|
||||
{
|
||||
const ChildStuff* p = (const ChildStuff*) arg;
|
||||
|
||||
/* Close the parent sides of the pipes.
|
||||
Closing pipe fds here is redundant, since closeDescriptors()
|
||||
would do it anyways, but a little paranoia is a good thing. */
|
||||
closeSafely(p->in[1]);
|
||||
closeSafely(p->out[0]);
|
||||
closeSafely(p->err[0]);
|
||||
closeSafely(p->fail[0]);
|
||||
|
||||
/* Give the child sides of the pipes the right fileno's. */
|
||||
/* Note: it is possible for in[0] == 0 */
|
||||
moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0], STDIN_FILENO);
|
||||
moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1], STDOUT_FILENO);
|
||||
|
||||
if (p->redirectErrorStream) {
|
||||
closeSafely(p->err[1]);
|
||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||
} else {
|
||||
moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2], STDERR_FILENO);
|
||||
}
|
||||
|
||||
moveDescriptor(p->fail[1], FAIL_FILENO);
|
||||
|
||||
/* close everything */
|
||||
if (closeDescriptors() == 0) { /* failed, close the old way */
|
||||
int max_fd = (int)sysconf(_SC_OPEN_MAX);
|
||||
int i;
|
||||
for (i = FAIL_FILENO + 1; i < max_fd; i++)
|
||||
close(i);
|
||||
}
|
||||
|
||||
/* change to the new working directory */
|
||||
if (p->pdir != NULL && chdir(p->pdir) < 0)
|
||||
goto WhyCantJohnnyExec;
|
||||
|
||||
if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1)
|
||||
goto WhyCantJohnnyExec;
|
||||
|
||||
execvpe(p->argv[0], p->argv, p->envv);
|
||||
|
||||
WhyCantJohnnyExec:
|
||||
/* We used to go to an awful lot of trouble to predict whether the
|
||||
* child would fail, but there is no reliable way to predict the
|
||||
* success of an operation without *trying* it, and there's no way
|
||||
* to try a chdir or exec in the parent. Instead, all we need is a
|
||||
* way to communicate any failure back to the parent. Easy; we just
|
||||
* send the errno back to the parent over a pipe in case of failure.
|
||||
* The tricky thing is, how do we communicate the *success* of exec?
|
||||
* We use FD_CLOEXEC together with the fact that a read() on a pipe
|
||||
* yields EOF when the write ends (we have two of them!) are closed.
|
||||
*/
|
||||
{
|
||||
int errnum = errno;
|
||||
write(FAIL_FILENO, &errnum, sizeof(errnum));
|
||||
}
|
||||
close(FAIL_FILENO);
|
||||
_exit(-1);
|
||||
return 0; /* Suppress warning "no return value from function" */
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
||||
@ -533,34 +666,43 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
||||
{
|
||||
int errnum;
|
||||
int resultPid = -1;
|
||||
#if USE_CLONE
|
||||
void *clone_stack = NULL;
|
||||
#endif
|
||||
int in[2], out[2], err[2], fail[2];
|
||||
const char **argv = NULL;
|
||||
const char **envv = NULL;
|
||||
const char *pprog = getBytes(env, prog);
|
||||
const char *pargBlock = getBytes(env, argBlock);
|
||||
const char *penvBlock = getBytes(env, envBlock);
|
||||
const char *pdir = getBytes(env, dir);
|
||||
jint *fds = NULL;
|
||||
const char *pprog = NULL;
|
||||
const char *pargBlock = NULL;
|
||||
const char *penvBlock = NULL;
|
||||
ChildStuff *c;
|
||||
|
||||
in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;
|
||||
|
||||
assert(prog != NULL && argBlock != NULL);
|
||||
if (pprog == NULL) goto Catch;
|
||||
if (pargBlock == NULL) goto Catch;
|
||||
if (envBlock != NULL && penvBlock == NULL) goto Catch;
|
||||
if (dir != NULL && pdir == NULL) goto Catch;
|
||||
if ((c = NEW(ChildStuff, 1)) == NULL) return -1;
|
||||
c->argv = NULL;
|
||||
c->envv = NULL;
|
||||
c->pdir = NULL;
|
||||
|
||||
/* Convert pprog + pargBlock into a char ** argv */
|
||||
if ((argv = NEW(const char *, argc + 2)) == NULL)
|
||||
goto Catch;
|
||||
argv[0] = pprog;
|
||||
initVectorFromBlock(argv+1, pargBlock, argc);
|
||||
/* Convert prog + argBlock into a char ** argv.
|
||||
* Add one word room for expansion of argv for use by
|
||||
* execve_as_traditional_shell_script.
|
||||
*/
|
||||
assert(prog != NULL && argBlock != NULL);
|
||||
if ((pprog = getBytes(env, prog)) == NULL) goto Catch;
|
||||
if ((pargBlock = getBytes(env, argBlock)) == NULL) goto Catch;
|
||||
if ((c->argv = NEW(const char *, argc + 3)) == NULL) goto Catch;
|
||||
c->argv[0] = pprog;
|
||||
initVectorFromBlock(c->argv+1, pargBlock, argc);
|
||||
|
||||
if (envBlock != NULL) {
|
||||
/* Convert penvBlock into a char ** envv */
|
||||
if ((envv = NEW(const char *, envc + 1)) == NULL)
|
||||
goto Catch;
|
||||
initVectorFromBlock(envv, penvBlock, envc);
|
||||
/* Convert envBlock into a char ** envv */
|
||||
if ((penvBlock = getBytes(env, envBlock)) == NULL) goto Catch;
|
||||
if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch;
|
||||
initVectorFromBlock(c->envv, penvBlock, envc);
|
||||
}
|
||||
|
||||
if (dir != NULL) {
|
||||
if ((c->pdir = getBytes(env, dir)) == NULL) goto Catch;
|
||||
}
|
||||
|
||||
assert(std_fds != NULL);
|
||||
@ -574,72 +716,45 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
||||
throwIOException(env, errno, "Bad file descriptor");
|
||||
goto Catch;
|
||||
}
|
||||
c->fds[0] = fds[0];
|
||||
c->fds[1] = fds[1];
|
||||
c->fds[2] = fds[2];
|
||||
|
||||
copyPipe(in, c->in);
|
||||
copyPipe(out, c->out);
|
||||
copyPipe(err, c->err);
|
||||
copyPipe(fail, c->fail);
|
||||
|
||||
c->redirectErrorStream = redirectErrorStream;
|
||||
|
||||
{
|
||||
#if USE_CLONE
|
||||
/* See clone(2).
|
||||
* Instead of worrying about which direction the stack grows, just
|
||||
* allocate twice as much and start the stack in the middle. */
|
||||
const int stack_size = 64 * 1024;
|
||||
if ((clone_stack = NEW(char, 2 * stack_size)) == NULL) goto Catch;
|
||||
resultPid = clone(childProcess, clone_stack + stack_size,
|
||||
/* CLONE_VFORK | // works, but unnecessary */
|
||||
CLONE_VM | SIGCHLD, c);
|
||||
#else
|
||||
/* From fork(2): In Solaris 10, a call to fork() is identical
|
||||
* to a call to fork1(); only the calling thread is replicated
|
||||
* in the child process. This is the POSIX-specified behavior
|
||||
* for fork(). */
|
||||
resultPid = fork();
|
||||
if (resultPid == 0) {
|
||||
childProcess(c);
|
||||
assert(0); /* childProcess must not return */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
resultPid = fork1();
|
||||
if (resultPid < 0) {
|
||||
throwIOException(env, errno, "Fork failed");
|
||||
goto Catch;
|
||||
}
|
||||
|
||||
if (resultPid == 0) {
|
||||
/* Child process */
|
||||
|
||||
/* Close the parent sides of the pipes.
|
||||
Closing pipe fds here is redundant, since closeDescriptors()
|
||||
would do it anyways, but a little paranoia is a good thing. */
|
||||
closeSafely(in[1]);
|
||||
closeSafely(out[0]);
|
||||
closeSafely(err[0]);
|
||||
closeSafely(fail[0]);
|
||||
|
||||
/* Give the child sides of the pipes the right fileno's. */
|
||||
/* Note: it is possible for in[0] == 0 */
|
||||
moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO);
|
||||
moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO);
|
||||
|
||||
if (redirectErrorStream) {
|
||||
closeSafely(err[1]);
|
||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||
} else {
|
||||
moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO);
|
||||
}
|
||||
|
||||
moveDescriptor(fail[1], FAIL_FILENO);
|
||||
|
||||
/* close everything */
|
||||
if (closeDescriptors() == 0) { /* failed, close the old way */
|
||||
int max_fd = (int)sysconf(_SC_OPEN_MAX);
|
||||
int i;
|
||||
for (i = FAIL_FILENO + 1; i < max_fd; i++)
|
||||
close(i);
|
||||
}
|
||||
|
||||
/* change to the new working directory */
|
||||
if (pdir != NULL && chdir(pdir) < 0)
|
||||
goto WhyCantJohnnyExec;
|
||||
|
||||
if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1)
|
||||
goto WhyCantJohnnyExec;
|
||||
|
||||
execvpe(argv[0], argv, envv);
|
||||
|
||||
WhyCantJohnnyExec:
|
||||
/* We used to go to an awful lot of trouble to predict whether the
|
||||
* child would fail, but there is no reliable way to predict the
|
||||
* success of an operation without *trying* it, and there's no way
|
||||
* to try a chdir or exec in the parent. Instead, all we need is a
|
||||
* way to communicate any failure back to the parent. Easy; we just
|
||||
* send the errno back to the parent over a pipe in case of failure.
|
||||
* The tricky thing is, how do we communicate the *success* of exec?
|
||||
* We use FD_CLOEXEC together with the fact that a read() on a pipe
|
||||
* yields EOF when the write ends (we have two of them!) are closed.
|
||||
*/
|
||||
errnum = errno;
|
||||
write(FAIL_FILENO, &errnum, sizeof(errnum));
|
||||
close(FAIL_FILENO);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
/* parent process */
|
||||
|
||||
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
|
||||
@ -660,6 +775,10 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
||||
fds[2] = (err[0] != -1) ? err[0] : -1;
|
||||
|
||||
Finally:
|
||||
#if USE_CLONE
|
||||
free(clone_stack);
|
||||
#endif
|
||||
|
||||
/* Always clean up the child's side of the pipes */
|
||||
closeSafely(in [0]);
|
||||
closeSafely(out[1]);
|
||||
@ -669,13 +788,14 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
||||
closeSafely(fail[0]);
|
||||
closeSafely(fail[1]);
|
||||
|
||||
free(argv);
|
||||
free(envv);
|
||||
|
||||
releaseBytes(env, prog, pprog);
|
||||
releaseBytes(env, argBlock, pargBlock);
|
||||
releaseBytes(env, envBlock, penvBlock);
|
||||
releaseBytes(env, dir, pdir);
|
||||
releaseBytes(env, dir, c->pdir);
|
||||
|
||||
free(c->argv);
|
||||
free(c->envv);
|
||||
free(c);
|
||||
|
||||
if (fds != NULL)
|
||||
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
|
||||
|
@ -257,6 +257,18 @@ public class Basic {
|
||||
s.write(bytes); // Might hang!
|
||||
}
|
||||
|
||||
static void checkPermissionDenied(ProcessBuilder pb) {
|
||||
try {
|
||||
pb.start();
|
||||
fail("Expected IOException not thrown");
|
||||
} catch (IOException e) {
|
||||
String m = e.getMessage();
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, "Permission denied"))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
public static class JavaChild {
|
||||
public static void main(String args[]) throws Throwable {
|
||||
String action = args[0];
|
||||
@ -317,12 +329,10 @@ public class Basic {
|
||||
for (final ProcessBuilder pb :
|
||||
new ProcessBuilder[] {pb1, pb2}) {
|
||||
pb.command("true");
|
||||
r = run(pb.start());
|
||||
equal(r.exitValue(), True.exitValue());
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
|
||||
pb.command("false");
|
||||
r = run(pb.start());
|
||||
equal(r.exitValue(), False.exitValue());
|
||||
equal(run(pb).exitValue(), False.exitValue());
|
||||
}
|
||||
|
||||
if (failed != 0) throw new Error("null PATH");
|
||||
@ -367,31 +377,82 @@ public class Basic {
|
||||
// Can't execute a directory -- permission denied
|
||||
// Report EACCES errno
|
||||
new File("dir1/prog").mkdirs();
|
||||
try {
|
||||
pb.start();
|
||||
fail("Expected IOException not thrown");
|
||||
} catch (IOException e) {
|
||||
String m = e.getMessage();
|
||||
if (EnglishUnix.is() &&
|
||||
! matches(m, "Permission denied"))
|
||||
unexpected(e);
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
checkPermissionDenied(pb);
|
||||
|
||||
// continue searching if EACCES
|
||||
copy("/bin/true", "dir2/prog");
|
||||
equal(run(pb.start()).exitValue(), True.exitValue());
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
new File("dir1/prog").delete();
|
||||
new File("dir2/prog").delete();
|
||||
|
||||
new File("dir2/prog").mkdirs();
|
||||
copy("/bin/true", "dir1/prog");
|
||||
equal(run(pb.start()).exitValue(), True.exitValue());
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
|
||||
// Check empty PATH component means current directory
|
||||
// Check empty PATH component means current directory.
|
||||
//
|
||||
// While we're here, let's test different kinds of
|
||||
// Unix executables, and PATH vs explicit searching.
|
||||
new File("dir1/prog").delete();
|
||||
new File("dir2/prog").delete();
|
||||
copy("/bin/true", "./prog");
|
||||
equal(run(pb.start()).exitValue(), True.exitValue());
|
||||
for (String[] command :
|
||||
new String[][] {
|
||||
new String[] {"./prog"},
|
||||
cmd}) {
|
||||
pb.command(command);
|
||||
File prog = new File("./prog");
|
||||
// "Normal" binaries
|
||||
copy("/bin/true", "./prog");
|
||||
equal(run(pb).exitValue(),
|
||||
True.exitValue());
|
||||
copy("/bin/false", "./prog");
|
||||
equal(run(pb).exitValue(),
|
||||
False.exitValue());
|
||||
prog.delete();
|
||||
// Interpreter scripts with #!
|
||||
setFileContents(prog, "#!/bin/true\n");
|
||||
prog.setExecutable(true);
|
||||
equal(run(pb).exitValue(),
|
||||
True.exitValue());
|
||||
prog.delete();
|
||||
setFileContents(prog, "#!/bin/false\n");
|
||||
prog.setExecutable(true);
|
||||
equal(run(pb).exitValue(),
|
||||
False.exitValue());
|
||||
// Traditional shell scripts without #!
|
||||
setFileContents(prog, "exec /bin/true\n");
|
||||
prog.setExecutable(true);
|
||||
equal(run(pb).exitValue(),
|
||||
True.exitValue());
|
||||
prog.delete();
|
||||
setFileContents(prog, "exec /bin/false\n");
|
||||
prog.setExecutable(true);
|
||||
equal(run(pb).exitValue(),
|
||||
False.exitValue());
|
||||
prog.delete();
|
||||
}
|
||||
|
||||
// Test Unix interpreter scripts
|
||||
File dir1Prog = new File("dir1/prog");
|
||||
dir1Prog.delete();
|
||||
pb.command(new String[] {"prog", "world"});
|
||||
setFileContents(dir1Prog, "#!/bin/echo hello\n");
|
||||
checkPermissionDenied(pb);
|
||||
dir1Prog.setExecutable(true);
|
||||
equal(run(pb).out(), "hello dir1/prog world\n");
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
dir1Prog.delete();
|
||||
pb.command(cmd);
|
||||
|
||||
// Test traditional shell scripts without #!
|
||||
setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
|
||||
pb.command(new String[] {"prog", "hello", "world"});
|
||||
checkPermissionDenied(pb);
|
||||
dir1Prog.setExecutable(true);
|
||||
equal(run(pb).out(), "hello world\n");
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
dir1Prog.delete();
|
||||
pb.command(cmd);
|
||||
|
||||
// If prog found on both parent and child's PATH,
|
||||
// parent's is used.
|
||||
@ -402,10 +463,10 @@ public class Basic {
|
||||
copy("/bin/true", "dir1/prog");
|
||||
copy("/bin/false", "dir3/prog");
|
||||
pb.environment().put("PATH","dir3");
|
||||
equal(run(pb.start()).exitValue(), True.exitValue());
|
||||
equal(run(pb).exitValue(), True.exitValue());
|
||||
copy("/bin/true", "dir3/prog");
|
||||
copy("/bin/false", "dir1/prog");
|
||||
equal(run(pb.start()).exitValue(), False.exitValue());
|
||||
equal(run(pb).exitValue(), False.exitValue());
|
||||
|
||||
} finally {
|
||||
// cleanup
|
||||
@ -1503,21 +1564,19 @@ public class Basic {
|
||||
childArgs.add("OutErr");
|
||||
ProcessBuilder pb = new ProcessBuilder(childArgs);
|
||||
{
|
||||
ProcessResults r = run(pb.start());
|
||||
ProcessResults r = run(pb);
|
||||
equal(r.out(), "outout");
|
||||
equal(r.err(), "errerr");
|
||||
}
|
||||
{
|
||||
pb.redirectErrorStream(true);
|
||||
ProcessResults r = run(pb.start());
|
||||
ProcessResults r = run(pb);
|
||||
equal(r.out(), "outerrouterr");
|
||||
equal(r.err(), "");
|
||||
}
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
|
||||
if (! Windows.is() &&
|
||||
new File("/bin/true").exists() &&
|
||||
new File("/bin/false").exists()) {
|
||||
if (Unix.is()) {
|
||||
//----------------------------------------------------------------
|
||||
// We can find true and false when PATH is null
|
||||
//----------------------------------------------------------------
|
||||
@ -1526,7 +1585,7 @@ public class Basic {
|
||||
childArgs.add("null PATH");
|
||||
ProcessBuilder pb = new ProcessBuilder(childArgs);
|
||||
pb.environment().remove("PATH");
|
||||
ProcessResults r = run(pb.start());
|
||||
ProcessResults r = run(pb);
|
||||
equal(r.out(), "");
|
||||
equal(r.err(), "");
|
||||
equal(r.exitValue(), 0);
|
||||
@ -1540,7 +1599,7 @@ public class Basic {
|
||||
childArgs.add("PATH search algorithm");
|
||||
ProcessBuilder pb = new ProcessBuilder(childArgs);
|
||||
pb.environment().put("PATH", "dir1:dir2:");
|
||||
ProcessResults r = run(pb.start());
|
||||
ProcessResults r = run(pb);
|
||||
equal(r.out(), "");
|
||||
equal(r.err(), "");
|
||||
equal(r.exitValue(), True.exitValue());
|
||||
|
103
jdk/test/java/lang/ProcessBuilder/BigFork.java
Normal file
103
jdk/test/java/lang/ProcessBuilder/BigFork.java
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A manual test that demonstrates the ability to start a subprocess
|
||||
* on Linux without getting ENOMEM. Run this test like:
|
||||
*
|
||||
* java -Xmx7000m BigFork
|
||||
*
|
||||
* providing a -Xmx flag suitable for your operating environment.
|
||||
* Here's the bad old behavior:
|
||||
*
|
||||
* ==> java -Xmx7000m -esa -ea BigFork
|
||||
* -------
|
||||
* CommitLimit: 6214700 kB
|
||||
* Committed_AS: 2484452 kB
|
||||
* -------
|
||||
* size=4.6GB
|
||||
* -------
|
||||
* CommitLimit: 6214700 kB
|
||||
* Committed_AS: 7219680 kB
|
||||
* -------
|
||||
* Exception in thread "main" java.io.IOException: Cannot run program "/bin/true": java.io.IOException: error=12, Cannot allocate memory
|
||||
* at java.lang.ProcessBuilder.start(ProcessBuilder.java:1018)
|
||||
* at BigFork.main(BigFork.java:79)
|
||||
* Caused by: java.io.IOException: java.io.IOException: error=12, Cannot allocate memory
|
||||
* at java.lang.UNIXProcess.<init>(UNIXProcess.java:190)
|
||||
* at java.lang.ProcessImpl.start(ProcessImpl.java:128)
|
||||
* at java.lang.ProcessBuilder.start(ProcessBuilder.java:1010)
|
||||
* ... 1 more
|
||||
*/
|
||||
public class BigFork {
|
||||
static final Random rnd = new Random();
|
||||
static void touchPages(byte[] chunk) {
|
||||
final int pageSize = 4096;
|
||||
for (int i = 0; i < chunk.length; i+= pageSize) {
|
||||
chunk[i] = (byte) rnd.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
static void showCommittedMemory() throws IOException {
|
||||
BufferedReader r =
|
||||
new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream("/proc/meminfo")));
|
||||
System.out.println("-------");
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("Commit")) {
|
||||
System.out.printf("%s%n", line);
|
||||
}
|
||||
}
|
||||
System.out.println("-------");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
showCommittedMemory();
|
||||
|
||||
final int chunkSize = 1024 * 1024 * 100;
|
||||
List<byte[]> chunks = new ArrayList<byte[]>(100);
|
||||
try {
|
||||
for (;;) {
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
touchPages(chunk);
|
||||
chunks.add(chunk);
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
chunks.set(0, null); // Free up one chunk
|
||||
System.gc();
|
||||
int size = chunks.size();
|
||||
System.out.printf("size=%.2gGB%n", (double)size/10);
|
||||
|
||||
showCommittedMemory();
|
||||
|
||||
// Can we fork/exec in our current bloated state?
|
||||
Process p = new ProcessBuilder("/bin/true").start();
|
||||
p.waitFor();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,6 +26,7 @@
|
||||
* @bug 6706974
|
||||
* @summary Add krb5 test infrastructure
|
||||
*/
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.Security;
|
||||
@ -50,17 +51,20 @@ public class CrossRealm implements CallbackHandler {
|
||||
KDC kdc1 = KDC.create("RABBIT.HOLE");
|
||||
kdc1.addPrincipal("dummy", "bogus".toCharArray());
|
||||
kdc1.addPrincipalRandKey("krbtgt/RABBIT.HOLE");
|
||||
kdc1.addPrincipal("krbtgt/SNAKE.HOLE", "sharedsec".toCharArray());
|
||||
kdc1.addPrincipal("krbtgt/SNAKE.HOLE@RABBIT.HOLE",
|
||||
"rabbit->snake".toCharArray());
|
||||
|
||||
KDC kdc2 = KDC.create("SNAKE.HOLE");
|
||||
kdc2.addPrincipalRandKey("krbtgt/SNAKE.HOLE");
|
||||
kdc2.addPrincipal("krbtgt/RABBIT.HOLE", "sharedsec".toCharArray());
|
||||
kdc2.addPrincipal("krbtgt/SNAKE.HOLE@RABBIT.HOLE",
|
||||
"rabbit->snake".toCharArray());
|
||||
kdc2.addPrincipalRandKey("host/www.snake.hole");
|
||||
|
||||
KDC.saveConfig("krb5-localkdc.conf", kdc1, kdc2,
|
||||
"forwardable=true",
|
||||
"[domain_realm]",
|
||||
".snake.hole=SNAKE.HOLE");
|
||||
new File("krb5-localkdc.conf").deleteOnExit();
|
||||
System.setProperty("java.security.krb5.conf", "krb5-localkdc.conf");
|
||||
}
|
||||
|
||||
@ -68,6 +72,7 @@ public class CrossRealm implements CallbackHandler {
|
||||
Security.setProperty("auth.login.defaultCallbackHandler", "CrossRealm");
|
||||
System.setProperty("java.security.auth.login.config", "jaas-localkdc.conf");
|
||||
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
|
||||
new File("jaas-localkdc.conf").deleteOnExit();
|
||||
FileOutputStream fos = new FileOutputStream("jaas-localkdc.conf");
|
||||
fos.write(("com.sun.security.jgss.krb5.initiate {\n" +
|
||||
" com.sun.security.auth.module.Krb5LoginModule\n" +
|
||||
|
@ -25,7 +25,6 @@
|
||||
* @test
|
||||
* @bug 6578647
|
||||
* @summary Undefined requesting URL in java.net.Authenticator.getPasswordAuthentication()
|
||||
* @run main/othervm -Dsun.net.spi.nameservice.provider.1=ns,mock HttpNegotiateServer
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.Headers;
|
||||
@ -40,12 +39,10 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.InetAddress;
|
||||
import java.net.PasswordAuthentication;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.security.auth.Subject;
|
||||
@ -53,8 +50,6 @@ import org.ietf.jgss.GSSContext;
|
||||
import org.ietf.jgss.GSSCredential;
|
||||
import org.ietf.jgss.GSSManager;
|
||||
import sun.security.jgss.GSSUtil;
|
||||
import sun.net.spi.nameservice.NameService;
|
||||
import sun.net.spi.nameservice.NameServiceDescriptor;
|
||||
import sun.security.krb5.Config;
|
||||
|
||||
/**
|
||||
@ -62,7 +57,7 @@ import sun.security.krb5.Config;
|
||||
* party uses JAAS login to get subjects and executes JGSS calls using
|
||||
* Subject.doAs.
|
||||
*/
|
||||
public class HttpNegotiateServer implements NameServiceDescriptor {
|
||||
public class HttpNegotiateServer {
|
||||
|
||||
// Two realm, web server in one, proxy server in another
|
||||
final static String REALM_WEB = "WEB.DOMAIN";
|
||||
@ -142,12 +137,12 @@ public class HttpNegotiateServer implements NameServiceDescriptor {
|
||||
public static void main(String[] args)
|
||||
throws Exception {
|
||||
|
||||
KDC kdcw = new KDC(REALM_WEB, 0, true);
|
||||
KDC kdcw = KDC.create(REALM_WEB);
|
||||
kdcw.addPrincipal(WEB_USER, WEB_PASS);
|
||||
kdcw.addPrincipalRandKey("krbtgt/" + REALM_WEB);
|
||||
kdcw.addPrincipalRandKey("HTTP/" + WEB_HOST);
|
||||
|
||||
KDC kdcp = new KDC(REALM_PROXY, 0, true);
|
||||
KDC kdcp = KDC.create(REALM_PROXY);
|
||||
kdcp.addPrincipal(PROXY_USER, PROXY_PASS);
|
||||
kdcp.addPrincipalRandKey("krbtgt/" + REALM_PROXY);
|
||||
kdcp.addPrincipalRandKey("HTTP/" + PROXY_HOST);
|
||||
@ -306,36 +301,5 @@ public class HttpNegotiateServer implements NameServiceDescriptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameService createNameService() throws Exception {
|
||||
NameService ns = new NameService() {
|
||||
@Override
|
||||
public InetAddress[] lookupAllHostAddr(String host)
|
||||
throws UnknownHostException {
|
||||
// Everything is localhost
|
||||
return new InetAddress[]{
|
||||
InetAddress.getByAddress(host, new byte[]{127,0,0,1})
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public String getHostByAddr(byte[] addr)
|
||||
throws UnknownHostException {
|
||||
// No reverse lookup
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
};
|
||||
return ns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderName() {
|
||||
return "mock";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "ns";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ import java.lang.reflect.Method;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import sun.net.spi.nameservice.NameService;
|
||||
import sun.net.spi.nameservice.NameServiceDescriptor;
|
||||
import sun.security.krb5.*;
|
||||
import sun.security.krb5.internal.*;
|
||||
import sun.security.krb5.internal.ccache.CredentialsCache;
|
||||
@ -118,14 +120,16 @@ public class KDC {
|
||||
|
||||
// The random generator to generate random keys (including session keys)
|
||||
private static SecureRandom secureRandom = new SecureRandom();
|
||||
// Principal db
|
||||
// Principal db. principal -> pass
|
||||
private Map<String,char[]> passwords = new HashMap<String,char[]>();
|
||||
// Realm name
|
||||
private String realm;
|
||||
// The request/response job queue
|
||||
private BlockingQueue<Job> q = new ArrayBlockingQueue<Job>(100);
|
||||
// KDC
|
||||
private String kdc;
|
||||
// Service port number
|
||||
private int port;
|
||||
// The request/response job queue
|
||||
private BlockingQueue<Job> q = new ArrayBlockingQueue<Job>(100);
|
||||
// Options
|
||||
private Map<Option,Object> options = new HashMap<Option,Object>();
|
||||
|
||||
@ -139,33 +143,21 @@ public class KDC {
|
||||
PREAUTH_REQUIRED,
|
||||
};
|
||||
|
||||
static {
|
||||
System.setProperty("sun.net.spi.nameservice.provider.1", "ns,mock");
|
||||
}
|
||||
|
||||
/**
|
||||
* A standalone KDC server.
|
||||
* @param args
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length > 0) {
|
||||
if (args[0].equals("-help") || args[0].equals("--help")) {
|
||||
System.out.println("Usage:");
|
||||
System.out.println(" java " + KDC.class + " " +
|
||||
"Start KDC on port 8888");
|
||||
return;
|
||||
}
|
||||
}
|
||||
String localhost = "localhost";
|
||||
try {
|
||||
localhost = InetAddress.getByName(localhost)
|
||||
.getCanonicalHostName();
|
||||
} catch (UnknownHostException uhe) {
|
||||
; // Ignore, localhost is still "localhost"
|
||||
}
|
||||
KDC kdc = create("RABBIT.HOLE", 8888, false);
|
||||
KDC kdc = create("RABBIT.HOLE", "kdc.rabbit,hole", 0, false);
|
||||
kdc.addPrincipal("dummy", "bogus".toCharArray());
|
||||
kdc.addPrincipal("foo", "bar".toCharArray());
|
||||
kdc.addPrincipalRandKey("krbtgt/" + kdc.realm);
|
||||
kdc.addPrincipalRandKey("server/" + localhost);
|
||||
kdc.addPrincipalRandKey("backend/" + localhost);
|
||||
kdc.addPrincipalRandKey("krbtgt/RABBIT.HOLE");
|
||||
kdc.addPrincipalRandKey("server/host.rabbit.hole");
|
||||
kdc.addPrincipalRandKey("backend/host.rabbit.hole");
|
||||
KDC.saveConfig("krb5.conf", kdc, "forwardable = true");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,7 +167,7 @@ public class KDC {
|
||||
* @throws java.io.IOException for any socket creation error
|
||||
*/
|
||||
public static KDC create(String realm) throws IOException {
|
||||
return create(realm, 0, true);
|
||||
return create(realm, "kdc." + realm.toLowerCase(), 0, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,8 +179,8 @@ public class KDC {
|
||||
* @return the running KDC instance
|
||||
* @throws java.io.IOException for any socket creation error
|
||||
*/
|
||||
public static KDC create(String realm, int port, boolean asDaemon) throws IOException {
|
||||
return new KDC(realm, port, asDaemon);
|
||||
public static KDC create(String realm, String kdc, int port, boolean asDaemon) throws IOException {
|
||||
return new KDC(realm, kdc, port, asDaemon);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,10 +220,7 @@ public class KDC {
|
||||
KeyTab ktab = KeyTab.create(tab);
|
||||
for (KDC kdc: kdcs) {
|
||||
for (String name : kdc.passwords.keySet()) {
|
||||
if (name.equals("krbtgt/" + kdc.realm)) {
|
||||
continue;
|
||||
}
|
||||
ktab.addEntry(new PrincipalName(name + "@" + kdc.realm,
|
||||
ktab.addEntry(new PrincipalName(name,
|
||||
name.indexOf('/') < 0 ?
|
||||
PrincipalName.KRB_NT_UNKNOWN :
|
||||
PrincipalName.KRB_NT_SRV_HST),
|
||||
@ -255,6 +244,9 @@ public class KDC {
|
||||
* @param pass the password for the principal
|
||||
*/
|
||||
public void addPrincipal(String user, char[] pass) {
|
||||
if (user.indexOf('@') < 0) {
|
||||
user = user + "@" + realm;
|
||||
}
|
||||
passwords.put(user, pass);
|
||||
}
|
||||
|
||||
@ -264,7 +256,7 @@ public class KDC {
|
||||
* form of host/f.q.d.n
|
||||
*/
|
||||
public void addPrincipalRandKey(String user) {
|
||||
passwords.put(user, randomPassword());
|
||||
addPrincipal(user, randomPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,6 +267,14 @@ public class KDC {
|
||||
return realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of kdc
|
||||
* @return the name of kdc
|
||||
*/
|
||||
public String getKDC() {
|
||||
return kdc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a krb5.conf for one or more KDC that includes KDC locations for
|
||||
* each realm and the default realm name. You can also add extra strings
|
||||
@ -299,7 +299,7 @@ public class KDC {
|
||||
*
|
||||
* [realms]
|
||||
* REALM.NAME = {
|
||||
* kdc = localhost:port_number
|
||||
* kdc = host:port_number
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
@ -320,10 +320,10 @@ public class KDC {
|
||||
*
|
||||
* [realms]
|
||||
* KDC1.NAME = {
|
||||
* kdc = localhost:port1
|
||||
* kdc = host:port1
|
||||
* }
|
||||
* KDC2.NAME = {
|
||||
* kdc = localhost:port2
|
||||
* kdc = host:port2
|
||||
* }
|
||||
* </pre>
|
||||
* @param file the name of the file to write into
|
||||
@ -372,16 +372,17 @@ public class KDC {
|
||||
* Private constructor, cannot be called outside.
|
||||
* @param realm
|
||||
*/
|
||||
private KDC(String realm) {
|
||||
private KDC(String realm, String kdc) {
|
||||
this.realm = realm;
|
||||
this.kdc = kdc;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor that starts the KDC service also.
|
||||
*/
|
||||
protected KDC(String realm, int port, boolean asDaemon)
|
||||
protected KDC(String realm, String kdc, int port, boolean asDaemon)
|
||||
throws IOException {
|
||||
this(realm);
|
||||
this(realm, kdc);
|
||||
startServer(port, asDaemon);
|
||||
}
|
||||
/**
|
||||
@ -426,7 +427,11 @@ public class KDC {
|
||||
* the database.
|
||||
*/
|
||||
private char[] getPassword(PrincipalName p) throws KrbException {
|
||||
char[] pass = passwords.get(p.getNameString());
|
||||
String pn = p.toString();
|
||||
if (p.getRealmString() == null) {
|
||||
pn = pn + "@" + getRealm();
|
||||
}
|
||||
char[] pass = passwords.get(pn);
|
||||
if (pass == null) {
|
||||
throw new KrbException(Krb5.KDC_ERR_C_PRINCIPAL_UNKNOWN);
|
||||
}
|
||||
@ -434,29 +439,18 @@ public class KDC {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the salt string for the principal. For normal users, the
|
||||
* concatenation for the realm name and the sections of the principal;
|
||||
* for krgtgt/A@B and krbtgt/B@A, always return AB (so that inter-realm
|
||||
* principals have the same key).
|
||||
* Returns the salt string for the principal.
|
||||
* @param p principal
|
||||
* @return the salt
|
||||
*/
|
||||
private String getSalt(PrincipalName p) {
|
||||
String[] ns = p.getNameStrings();
|
||||
if (ns[0].equals("krbtgt") && ns.length > 1) {
|
||||
// Shared cross-realm keys must be the same
|
||||
if (ns[1].compareTo(realm) < 0) {
|
||||
return ns[1] + realm;
|
||||
} else {
|
||||
return realm + ns[1];
|
||||
}
|
||||
} else {
|
||||
String s = getRealm();
|
||||
for (String n: p.getNameStrings()) {
|
||||
s += n;
|
||||
}
|
||||
return s;
|
||||
String s = p.getRealmString();
|
||||
if (s == null) s = getRealm();
|
||||
for (String n: p.getNameStrings()) {
|
||||
s += n;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -525,14 +519,8 @@ public class KDC {
|
||||
EncryptedData ed = apReq.authenticator;
|
||||
tkt = apReq.ticket;
|
||||
etype = tkt.encPart.getEType();
|
||||
EncryptionKey kkey = null;
|
||||
if (!tkt.realm.toString().equals(realm)) {
|
||||
if (tkt.sname.getNameString().equals("krbtgt/" + realm)) {
|
||||
kkey = keyForUser(new PrincipalName("krbtgt/" + tkt.realm.toString(), realm), etype);
|
||||
}
|
||||
} else {
|
||||
kkey = keyForUser(tkt.sname, etype);
|
||||
}
|
||||
tkt.sname.setRealm(tkt.realm);
|
||||
EncryptionKey kkey = keyForUser(tkt.sname, etype);
|
||||
byte[] bb = tkt.encPart.decrypt(kkey, KeyUsage.KU_TICKET);
|
||||
DerInputStream derIn = new DerInputStream(bb);
|
||||
DerValue der = derIn.getDerValue();
|
||||
@ -857,10 +845,13 @@ public class KDC {
|
||||
/**
|
||||
* Generates a line for a KDC to put inside [realms] of krb5.conf
|
||||
* @param kdc the KDC
|
||||
* @return REALM.NAME = { kdc = localhost:port }
|
||||
* @return REALM.NAME = { kdc = host:port }
|
||||
*/
|
||||
private static String realmLineForKDC(KDC kdc) {
|
||||
return String.format(" %s = {\n kdc = localhost:%d\n }\n", kdc.realm, kdc.port);
|
||||
return String.format(" %s = {\n kdc = %s:%d\n }\n",
|
||||
kdc.realm,
|
||||
kdc.kdc,
|
||||
kdc.port);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1000,4 +991,37 @@ public class KDC {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class KDCNameService implements NameServiceDescriptor {
|
||||
@Override
|
||||
public NameService createNameService() throws Exception {
|
||||
NameService ns = new NameService() {
|
||||
@Override
|
||||
public InetAddress[] lookupAllHostAddr(String host)
|
||||
throws UnknownHostException {
|
||||
// Everything is localhost
|
||||
return new InetAddress[]{
|
||||
InetAddress.getByAddress(host, new byte[]{127,0,0,1})
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public String getHostByAddr(byte[] addr)
|
||||
throws UnknownHostException {
|
||||
// No reverse lookup, PrincipalName use original string
|
||||
throw new UnknownHostException();
|
||||
}
|
||||
};
|
||||
return ns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderName() {
|
||||
return "mock";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "ns";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
HttpNegotiateServer
|
||||
KDC$KDCNameService
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,35 +46,22 @@ import sun.security.krb5.Config;
|
||||
*/
|
||||
public class OneKDC extends KDC {
|
||||
|
||||
// The krb5 codes would try to canonicalize hostnames before creating
|
||||
// a service principal name, so let's find out the canonicalized form
|
||||
// of localhost first. The following codes mimic the process inside
|
||||
// PrincipalName.java.
|
||||
static String localhost = "localhost";
|
||||
static {
|
||||
try {
|
||||
localhost = InetAddress.getByName(localhost)
|
||||
.getCanonicalHostName();
|
||||
} catch (UnknownHostException uhe) {
|
||||
; // Ignore, localhost is still "localhost"
|
||||
}
|
||||
}
|
||||
public static final String USER = "dummy";
|
||||
public static final char[] PASS = "bogus".toCharArray();
|
||||
public static String SERVER = "server/" + localhost;
|
||||
public static String BACKEND = "backend/" + localhost;
|
||||
public static final String KRB5_CONF = "localkdc-krb5.conf";
|
||||
public static final String KTAB = "localkdc.ktab";
|
||||
public static final String JAAS_CONF = "localkdc-jaas.conf";
|
||||
public static final String REALM = "RABBIT.HOLE";
|
||||
|
||||
public static String SERVER = "server/host." + REALM.toLowerCase();
|
||||
public static String BACKEND = "backend/host." + REALM.toLowerCase();
|
||||
public static String KDCHOST = "kdc." + REALM.toLowerCase();
|
||||
/**
|
||||
* Creates the KDC and starts it.
|
||||
* @param etype Encryption type, null if not specified
|
||||
* @throws java.lang.Exception if there's anything wrong
|
||||
*/
|
||||
public OneKDC(String etype) throws Exception {
|
||||
super(REALM, 0, true);
|
||||
super(REALM, KDCHOST, 0, true);
|
||||
addPrincipal(USER, PASS);
|
||||
addPrincipalRandKey("krbtgt/" + REALM);
|
||||
addPrincipalRandKey(SERVER);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
# Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,25 +41,31 @@ OS=`uname -s`
|
||||
case "$OS" in
|
||||
Windows_* )
|
||||
FS="\\"
|
||||
SEP=";"
|
||||
;;
|
||||
* )
|
||||
FS="/"
|
||||
SEP=":"
|
||||
;;
|
||||
esac
|
||||
|
||||
${TESTJAVA}${FS}bin${FS}javac -d . \
|
||||
${TESTJAVA}${FS}bin${FS}javac -XDignore.symbol.file -d . \
|
||||
${TESTSRC}${FS}BasicKrb5Test.java \
|
||||
${TESTSRC}${FS}KDC.java \
|
||||
${TESTSRC}${FS}OneKDC.java \
|
||||
${TESTSRC}${FS}Action.java \
|
||||
${TESTSRC}${FS}Context.java \
|
||||
|| exit 10
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test || exit 100
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des-cbc-crc || exit 1
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des-cbc-md5 || exit 3
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test des3-cbc-sha1 || exit 16
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test aes128-cts || exit 17
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test aes256-cts || exit 18
|
||||
${TESTJAVA}${FS}bin${FS}java -Dtest.src=$TESTSRC BasicKrb5Test rc4-hmac || exit 23
|
||||
|
||||
# Add $TESTSRC to classpath so that customized nameservice can be used
|
||||
J="${TESTJAVA}${FS}bin${FS}java -cp $TESTSRC${SEP}. BasicKrb5Test"
|
||||
|
||||
$J || exit 100
|
||||
$J des-cbc-crc || exit 1
|
||||
$J des-cbc-md5 || exit 3
|
||||
$J des3-cbc-sha1 || exit 16
|
||||
$J aes128-cts || exit 17
|
||||
$J aes256-cts || exit 18
|
||||
$J rc4-hmac || exit 23
|
||||
|
||||
exit 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user