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 }