1 /*
   2  * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include <sys/types.h>
  26 #include <sys/stat.h>
  27 #include <door.h>
  28 #include <stdlib.h>
  29 #include <unistd.h>
  30 #include <signal.h>
  31 #include <string.h>
  32 #include <fcntl.h>
  33 #include <errno.h>
  34 #include <limits.h>
  35 
  36 #include "jni.h"
  37 #include "jni_util.h"
  38 #include "jvm.h"
  39 
  40 #include "sun_tools_attach_VirtualMachineImpl.h"
  41 
  42 #define RESTARTABLE(_cmd, _result) do { \
  43   do { \
  44     _result = _cmd; \
  45   } while((_result == -1) && (errno == EINTR)); \
  46 } while(0)
  47 
  48 /*
  49  * Class:     sun_tools_attach_VirtualMachineImpl
  50  * Method:    open
  51  * Signature: (Ljava/lang/String;)I
  52  */
  53 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_open
  54   (JNIEnv *env, jclass cls, jstring path)
  55 {
  56     jboolean isCopy;
  57     const char* p = GetStringPlatformChars(env, path, &isCopy);
  58     if (p == NULL) {
  59         return 0;
  60     } else {
  61         int fd;
  62         int err = 0;
  63 
  64         fd = open(p, O_RDWR);
  65         if (fd == -1) {
  66             err = errno;
  67         }
  68 
  69         if (isCopy) {
  70             JNU_ReleaseStringPlatformChars(env, path, p);
  71         }
  72 
  73         if (fd == -1) {
  74             if (err == ENOENT) {
  75                 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
  76             } else {
  77                 char* msg = strdup(strerror(err));
  78                 JNU_ThrowIOException(env, msg);
  79                 if (msg != NULL) {
  80                     free(msg);
  81                 }
  82             }
  83         }
  84         return fd;
  85     }
  86 }
  87 
  88 /*
  89  * Class:     sun_tools_attach_VirtualMachineImpl
  90  * Method:    checkPermissions
  91  * Signature: (Ljava/lang/String;)V
  92  */
  93 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
  94   (JNIEnv *env, jclass cls, jstring path)
  95 {
  96     jboolean isCopy;
  97     const char* p = GetStringPlatformChars(env, path, &isCopy);
  98     if (p != NULL) {
  99         struct stat64 sb;
 100         uid_t uid, gid;
 101         int res;
 102 
 103         /*
 104          * Check that the path is owned by the effective uid/gid of this
 105          * process. Also check that group/other access is not allowed.
 106          */
 107         uid = geteuid();
 108         gid = getegid();
 109 
 110         res = stat64(p, &sb);
 111         if (res != 0) {
 112             /* save errno */
 113             res = errno;
 114         }
 115 
 116         if (res == 0) {
 117             char msg[100];
 118             jboolean isError = JNI_FALSE;
 119             if (sb.st_uid != uid) {
 120                 jio_snprintf(msg, sizeof(msg)-1,
 121                     "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
 122                 isError = JNI_TRUE;
 123             } else if (sb.st_gid != gid) {
 124                 jio_snprintf(msg, sizeof(msg)-1,
 125                     "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
 126                 isError = JNI_TRUE;
 127             } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
 128                 jio_snprintf(msg, sizeof(msg)-1,
 129                     "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
 130                 isError = JNI_TRUE;
 131             }
 132             if (isError) {
 133                 char buf[256];
 134                 jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);
 135                 JNU_ThrowIOException(env, buf);
 136             }
 137         } else {
 138             char* msg = strdup(strerror(res));
 139             JNU_ThrowIOException(env, msg);
 140             if (msg != NULL) {
 141                 free(msg);
 142             }
 143         }
 144 
 145         if (isCopy) {
 146             JNU_ReleaseStringPlatformChars(env, path, p);
 147         }
 148     }
 149 }
 150 
 151 /*
 152  * Class:     sun_tools_attach_VirtualMachineImpl
 153  * Method:    close
 154  * Signature: (I)V
 155  */
 156 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
 157   (JNIEnv *env, jclass cls, jint fd)
 158 {
 159     int ret;
 160     RESTARTABLE(close(fd), ret);
 161 }
 162 
 163 /*
 164  * Class:     sun_tools_attach_VirtualMachineImpl
 165  * Method:    read
 166  * Signature: (I[BI)I
 167  */
 168 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
 169   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
 170 {
 171     unsigned char buf[128];
 172     size_t len = sizeof(buf);
 173     ssize_t n;
 174 
 175     size_t remaining = (size_t)(baLen - off);
 176     if (len > remaining) {
 177         len = remaining;
 178     }
 179 
 180     RESTARTABLE(read(fd, buf, len), n);
 181     if (n == -1) {
 182         JNU_ThrowIOExceptionWithLastError(env, "read");
 183     } else {
 184         if (n == 0) {
 185             n = -1;     // EOF
 186         } else {
 187             (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
 188         }
 189     }
 190     return n;
 191 }
 192 
 193 /*
 194  * Class:     sun_tools_attach_VirtualMachineImpl
 195  * Method:    sigquit
 196  * Signature: (I)V
 197  */
 198 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sigquit
 199   (JNIEnv *env, jclass cls, jint pid)
 200 {
 201     if (kill((pid_t)pid, SIGQUIT) == -1) {
 202         JNU_ThrowIOExceptionWithLastError(env, "kill");
 203     }
 204 }
 205 
 206 /*
 207  * A simple table to translate some known errors into reasonable
 208  * error messages
 209  */
 210 static struct {
 211     jint err;
 212     const char* msg;
 213 } const error_messages[] = {
 214     { 100,      "Bad request" },
 215     { 101,      "Protocol mismatch" },
 216     { 102,      "Resource failure" },
 217     { 103,      "Internal error" },
 218     { 104,      "Permission denied" },
 219 };
 220 
 221 /*
 222  * Lookup the given error code and return the appropriate
 223  * message. If not found return NULL.
 224  */
 225 static const char* translate_error(jint err) {
 226     int table_size = sizeof(error_messages) / sizeof(error_messages[0]);
 227     int i;
 228 
 229     for (i=0; i<table_size; i++) {
 230         if (err == error_messages[i].err) {
 231             return error_messages[i].msg;
 232         }
 233     }
 234     return NULL;
 235 }
 236 
 237 /*
 238  * Current protocol version
 239  */
 240 static const char* PROTOCOL_VERSION = "1";
 241 
 242 /*
 243  * Class:     sun_tools_attach_VirtualMachineImpl
 244  * Method:    enqueue
 245  * Signature: (JILjava/lang/String;[Ljava/lang/Object;)V
 246  */
 247 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue
 248   (JNIEnv *env, jclass cls, jint fd, jstring cmd, jobjectArray args)
 249 {
 250     jint arg_count, i;
 251     size_t size;
 252     jboolean isCopy;
 253     door_arg_t door_args;
 254     char res_buffer[128];
 255     jint result = -1;
 256     int rc;
 257     const char* cstr;
 258     char* buf;
 259 
 260     /*
 261      * First we get the command string and create the start of the
 262      * argument string to send to the target VM:
 263      * <ver>\0<cmd>\0
 264      */
 265     cstr = JNU_GetStringPlatformChars(env, cmd, &isCopy);
 266     if (cstr == NULL) {
 267         return -1;              /* pending exception */
 268     }
 269     size = strlen(PROTOCOL_VERSION) + strlen(cstr) + 2;
 270     buf = (char*)malloc(size);
 271     if (buf != NULL) {
 272         char* pos = buf;
 273         strcpy(buf, PROTOCOL_VERSION);
 274         pos += strlen(PROTOCOL_VERSION)+1;
 275         strcpy(pos, cstr);
 276     }
 277     if (isCopy) {
 278         JNU_ReleaseStringPlatformChars(env, cmd, cstr);
 279     }
 280     if (buf == NULL) {
 281         JNU_ThrowOutOfMemoryError(env, "malloc failed");
 282         return -1;
 283     }
 284 
 285     /*
 286      * Next we iterate over the arguments and extend the buffer
 287      * to include them.
 288      */
 289     arg_count = (*env)->GetArrayLength(env, args);
 290 
 291     for (i=0; i<arg_count; i++) {
 292         jobject obj = (*env)->GetObjectArrayElement(env, args, i);
 293         if (obj != NULL) {
 294             cstr = JNU_GetStringPlatformChars(env, obj, &isCopy);
 295             if (cstr != NULL) {
 296                 size_t len = strlen(cstr);
 297                 char* newbuf = (char*)realloc(buf, size+len+1);
 298                 if (newbuf != NULL) {
 299                     buf = newbuf;
 300                     strcpy(buf+size, cstr);
 301                     size += len+1;
 302                 }
 303                 if (isCopy) {
 304                     JNU_ReleaseStringPlatformChars(env, obj, cstr);
 305                 }
 306                 if (newbuf == NULL) {
 307                     free(buf);
 308                     JNU_ThrowOutOfMemoryError(env, "realloc failed");
 309                     return -1;
 310                 }
 311             }
 312         }
 313         if ((*env)->ExceptionOccurred(env)) {
 314             free(buf);
 315             return -1;
 316         }
 317     }
 318 
 319     /*
 320      * The arguments to the door function are in 'buf' so we now
 321      * do the door call
 322      */
 323     door_args.data_ptr = buf;
 324     door_args.data_size = size;
 325     door_args.desc_ptr = NULL;
 326     door_args.desc_num = 0;
 327     door_args.rbuf = (char*)&res_buffer;
 328     door_args.rsize = sizeof(res_buffer);
 329 
 330     RESTARTABLE(door_call(fd, &door_args), rc);
 331 
 332     /*
 333      * door_call failed
 334      */
 335     if (rc == -1) {
 336         JNU_ThrowIOExceptionWithLastError(env, "door_call");
 337     } else {
 338         /*
 339          * door_call succeeded but the call didn't return the expected jint.
 340          */
 341         if (door_args.data_size < sizeof(jint)) {
 342             JNU_ThrowIOException(env, "Enqueue error - reason unknown as result is truncated!");
 343         } else {
 344             jint* res = (jint*)(door_args.data_ptr);
 345             if (*res != JNI_OK) {
 346                 const char* msg = translate_error(*res);
 347                 char buf[255];
 348                 if (msg == NULL) {
 349                     sprintf(buf, "Unable to enqueue command to target VM: %d", *res);
 350                 } else {
 351                     sprintf(buf, "Unable to enqueue command to target VM: %s", msg);
 352                 }
 353                 JNU_ThrowIOException(env, buf);
 354             } else {
 355                 /*
 356                  * The door call should return a file descriptor to one end of
 357                  * a socket pair
 358                  */
 359                 if ((door_args.desc_ptr != NULL) &&
 360                     (door_args.desc_num == 1) &&
 361                     (door_args.desc_ptr->d_attributes & DOOR_DESCRIPTOR)) {
 362                     result = door_args.desc_ptr->d_data.d_desc.d_descriptor;
 363                 } else {
 364                     JNU_ThrowIOException(env, "Reply from enqueue missing descriptor!");
 365                 }
 366             }
 367         }
 368     }
 369 
 370     free(buf);
 371     return result;
 372 }