|
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_createAttachFile0(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 } |