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