1 /* 2 * Copyright (c) 2005, 2015, 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.h" 27 #include "jni_util.h" 28 #include "jvm.h" 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <unistd.h> 35 #include <signal.h> 36 #include <dirent.h> 37 #include <ctype.h> 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 #include <sys/stat.h> 41 #include <sys/syslimits.h> 42 #include <sys/un.h> 43 #include <fcntl.h> 44 45 #include "sun_tools_attach_VirtualMachineImpl.h" 46 47 #define RESTARTABLE(_cmd, _result) do { \ 48 do { \ 49 _result = _cmd; \ 50 } while((_result == -1) && (errno == EINTR)); \ 51 } while(0) 52 53 /* 54 * Declare library specific JNI_Onload entry if static build 55 */ 56 DEF_STATIC_JNI_OnLoad 57 58 /* 59 * Class: sun_tools_attach_VirtualMachineImpl 60 * Method: socket 61 * Signature: ()I 62 */ 63 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket 64 (JNIEnv *env, jclass cls) 65 { 66 int fd = socket(PF_UNIX, SOCK_STREAM, 0); 67 if (fd == -1) { 68 JNU_ThrowIOExceptionWithLastError(env, "socket"); 69 } 70 return (jint)fd; 71 } 72 73 /* 74 * Class: sun_tools_attach_VirtualMachineImpl 75 * Method: connect 76 * Signature: (ILjava/lang/String;)I 77 */ 78 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect 79 (JNIEnv *env, jclass cls, jint fd, jstring path) 80 { 81 jboolean isCopy; 82 const char* p = GetStringPlatformChars(env, path, &isCopy); 83 if (p != NULL) { 84 struct sockaddr_un addr; 85 int err = 0; 86 87 memset(&addr, 0, sizeof(addr)); 88 addr.sun_family = AF_UNIX; 89 /* strncpy is safe because addr.sun_path was zero-initialized before. */ 90 strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1); 91 92 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { 93 err = errno; 94 } 95 96 if (isCopy) { 97 JNU_ReleaseStringPlatformChars(env, path, p); 98 } 99 100 /* 101 * If the connect failed then we throw the appropriate exception 102 * here (can't throw it before releasing the string as can't call 103 * JNI with pending exception) 104 */ 105 if (err != 0) { 106 if (err == ENOENT) { 107 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL); 108 } else { 109 char* msg = strdup(strerror(err)); 110 JNU_ThrowIOException(env, msg); 111 if (msg != NULL) { 112 free(msg); 113 } 114 } 115 } 116 } 117 } 118 119 /* 120 * Class: sun_tools_attach_VirtualMachineImpl 121 * Method: sendQuitTo 122 * Signature: (I)V 123 */ 124 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo 125 (JNIEnv *env, jclass cls, jint pid) 126 { 127 if (kill((pid_t)pid, SIGQUIT)) { 128 JNU_ThrowIOExceptionWithLastError(env, "kill"); 129 } 130 } 131 132 /* 133 * Class: sun_tools_attach_VirtualMachineImpl 134 * Method: checkPermissions 135 * Signature: (Ljava/lang/String;)V 136 */ 137 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions 138 (JNIEnv *env, jclass cls, jstring path) 139 { 140 jboolean isCopy; 141 const char* p = GetStringPlatformChars(env, path, &isCopy); 142 if (p != NULL) { 143 struct stat sb; 144 uid_t uid, gid; 145 int res; 146 147 /* 148 * Check that the path is owned by the effective uid/gid of this 149 * process. Also check that group/other access is not allowed. 150 */ 151 uid = geteuid(); 152 gid = getegid(); 153 154 res = stat(p, &sb); 155 if (res != 0) { 156 /* save errno */ 157 res = errno; 158 } 159 160 if (res == 0) { 161 char msg[100]; 162 jboolean isError = JNI_FALSE; 163 if (sb.st_uid != uid) { 164 jio_snprintf(msg, sizeof(msg)-1, 165 "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid); 166 isError = JNI_TRUE; 167 } else if (sb.st_gid != gid) { 168 jio_snprintf(msg, sizeof(msg)-1, 169 "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid); 170 isError = JNI_TRUE; 171 } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) { 172 jio_snprintf(msg, sizeof(msg)-1, 173 "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777); 174 isError = JNI_TRUE; 175 } 176 if (isError) { 177 char buf[256]; 178 jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg); 179 JNU_ThrowIOException(env, buf); 180 } 181 } else { 182 char* msg = strdup(strerror(res)); 183 JNU_ThrowIOException(env, msg); 184 if (msg != NULL) { 185 free(msg); 186 } 187 } 188 189 if (isCopy) { 190 JNU_ReleaseStringPlatformChars(env, path, p); 191 } 192 } 193 } 194 195 /* 196 * Class: sun_tools_attach_VirtualMachineImpl 197 * Method: close 198 * Signature: (I)V 199 */ 200 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close 201 (JNIEnv *env, jclass cls, jint fd) 202 { 203 int res; 204 RESTARTABLE(close(fd), res); 205 } 206 207 /* 208 * Class: sun_tools_attach_VirtualMachineImpl 209 * Method: read 210 * Signature: (I[BI)I 211 */ 212 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read 213 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen) 214 { 215 unsigned char buf[128]; 216 size_t len = sizeof(buf); 217 ssize_t n; 218 219 size_t remaining = (size_t)(baLen - off); 220 if (len > remaining) { 221 len = remaining; 222 } 223 224 RESTARTABLE(read(fd, buf, len), n); 225 if (n == -1) { 226 JNU_ThrowIOExceptionWithLastError(env, "read"); 227 } else { 228 if (n == 0) { 229 n = -1; // EOF 230 } else { 231 (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf)); 232 } 233 } 234 return n; 235 } 236 237 /* 238 * Class: sun_tools_attach_VirtualMachineImpl 239 * Method: write 240 * Signature: (I[B)V 241 */ 242 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write 243 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen) 244 { 245 size_t remaining = bufLen; 246 do { 247 unsigned char buf[128]; 248 size_t len = sizeof(buf); 249 int n; 250 251 if (len > remaining) { 252 len = remaining; 253 } 254 (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf); 255 256 RESTARTABLE(write(fd, buf, len), n); 257 if (n > 0) { 258 off += n; 259 remaining -= n; 260 } else { 261 JNU_ThrowIOExceptionWithLastError(env, "write"); 262 return; 263 } 264 265 } while (remaining > 0); 266 } 267 268 /* 269 * Class: sun_tools_attach_BSDVirtualMachine 270 * Method: createAttachFile 271 * Signature: (Ljava.lang.String;)V 272 */ 273 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile(JNIEnv *env, jclass cls, jstring path) 274 { 275 const char* _path; 276 jboolean isCopy; 277 int fd, rc; 278 279 _path = GetStringPlatformChars(env, path, &isCopy); 280 if (_path == NULL) { 281 JNU_ThrowIOException(env, "Must specify a path"); 282 return; 283 } 284 285 RESTARTABLE(open(_path, O_CREAT | O_EXCL, S_IWUSR | S_IRUSR), fd); 286 if (fd == -1) { 287 /* release p here before we throw an I/O exception */ 288 if (isCopy) { 289 JNU_ReleaseStringPlatformChars(env, path, _path); 290 } 291 JNU_ThrowIOExceptionWithLastError(env, "open"); 292 return; 293 } 294 295 RESTARTABLE(chown(_path, geteuid(), getegid()), rc); 296 297 RESTARTABLE(close(fd), rc); 298 299 /* release p here */ 300 if (isCopy) { 301 JNU_ReleaseStringPlatformChars(env, path, _path); 302 } 303 } 304 305 /* 306 * Class: sun_tools_attach_BSDVirtualMachine 307 * Method: getTempDir 308 * Signature: (V)Ljava.lang.String; 309 */ 310 JNIEXPORT jstring JNICALL Java_sun_tools_attach_VirtualMachineImpl_getTempDir(JNIEnv *env, jclass cls) 311 { 312 // This must be hard coded because it's the system's temporary 313 // directory not the java application's temp directory, ala java.io.tmpdir. 314 315 #ifdef __APPLE__ 316 // macosx has a secure per-user temporary directory 317 static char *temp_path = NULL; 318 char temp_path_storage[PATH_MAX]; 319 if (temp_path == NULL) { 320 int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, temp_path_storage, PATH_MAX); 321 if (pathSize == 0 || pathSize > PATH_MAX) { 322 strlcpy(temp_path_storage, "/tmp", sizeof(temp_path_storage)); 323 } 324 temp_path = temp_path_storage; 325 } 326 return JNU_NewStringPlatform(env, temp_path); 327 #else /* __APPLE__ */ 328 return (*env)->NewStringUTF(env, "/tmp"); 329 #endif /* __APPLE__ */ 330 }