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