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/socket.h>
29#include <sys/stat.h>
30#include <sys/types.h>
31#include <sys/un.h>
32#include <errno.h>
33#include <signal.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#include "sun_tools_attach_VirtualMachineImpl.h"
40
41#define RESTARTABLE(_cmd, _result) do { \
42 do { \
43 _result = _cmd; \
44 } while((_result == -1) && (errno == EINTR)); \
45} while(0)
46
47#define ROOT_UID 0
48
49/*
50 * Declare library specific JNI_Onload entry if static build
51 */
52DEF_STATIC_JNI_OnLoad
53
54/*
55 * Class: sun_tools_attach_VirtualMachineImpl
56 * Method: socket
57 * Signature: ()I
58 */
59JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_socket
60 (JNIEnv *env, jclass cls)
61{
62 int fd = socket(PF_UNIX, SOCK_STREAM, 0);
63 if (fd == -1) {
64 JNU_ThrowIOExceptionWithLastError(env, "socket");
65 }
66 return (jint)fd;
67}
68
69/*
70 * Class: sun_tools_attach_VirtualMachineImpl
71 * Method: connect
72 * Signature: (ILjava/lang/String;)I
73 */
74JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
75 (JNIEnv *env, jclass cls, jint fd, jstring path)
76{
77 jboolean isCopy;
78 const char* p = GetStringPlatformChars(env, path, &isCopy);
79 if (p != NULL) {
80 struct sockaddr_un addr;
81 int err = 0;
82
83 memset(&addr, 0, sizeof(addr));
84 addr.sun_family = AF_UNIX;
85 /* strncpy is safe because addr.sun_path was zero-initialized before. */
86 strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
87
88 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
89 err = errno;
90 }
91
92 if (isCopy) {
93 JNU_ReleaseStringPlatformChars(env, path, p);
94 }
95
96 /*
97 * If the connect failed then we throw the appropriate exception
98 * here (can't throw it before releasing the string as can't call
99 * JNI with pending exception)
100 */
101 if (err != 0) {
102 if (err == ENOENT) {
103 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
104 } else {
105 char* msg = strdup(strerror(err));
106 JNU_ThrowIOException(env, msg);
107 if (msg != NULL) {
108 free(msg);
109 }
110 }
111 }
112 }
113}
114
115/*
116 * Class: sun_tools_attach_VirtualMachineImpl
117 * Method: sendQuitTo
118 * Signature: (I)V
119 */
120JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
121 (JNIEnv *env, jclass cls, jint pid)
122{
123 if (kill((pid_t)pid, SIGQUIT)) {
124 JNU_ThrowIOExceptionWithLastError(env, "kill");
125 }
126}
127
128/*
129 * Class: sun_tools_attach_VirtualMachineImpl
130 * Method: checkPermissions
131 * Signature: (Ljava/lang/String;)V
132 */
133JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
134 (JNIEnv *env, jclass cls, jstring path)
135{
136 jboolean isCopy;
137 const char* p = GetStringPlatformChars(env, path, &isCopy);
138 if (p != NULL) {
139 struct stat64 sb;
140 uid_t uid, gid;
141 int res;
142
143 memset(&sb, 0, sizeof(struct stat64));
144
145 /*
146 * Check that the path is owned by the effective uid/gid of this
147 * process. Also check that group/other access is not allowed.
148 */
149 uid = geteuid();
150 gid = getegid();
151
152 res = stat64(p, &sb);
153 if (res != 0) {
154 /* save errno */
155 res = errno;
156 }
157
158 if (res == 0) {
159 char msg[100];
160 jboolean isError = JNI_FALSE;
161 if (sb.st_uid != uid && uid != ROOT_UID) {
162 snprintf(msg, sizeof(msg),
163 "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
164 isError = JNI_TRUE;
165 } else if (sb.st_gid != gid && uid != ROOT_UID) {
166 snprintf(msg, sizeof(msg),
167 "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
168 isError = JNI_TRUE;
169 } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
170 snprintf(msg, sizeof(msg),
171 "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
172 isError = JNI_TRUE;
173 }
174 if (isError) {
175 char buf[256];
176 snprintf(buf, sizeof(buf), "well-known file %s is not secure: %s", p, msg);
177 JNU_ThrowIOException(env, buf);
178 }
179 } else {
180 char* msg = strdup(strerror(res));
181 JNU_ThrowIOException(env, msg);
182 if (msg != NULL) {
183 free(msg);
184 }
185 }
186
187 if (isCopy) {
188 JNU_ReleaseStringPlatformChars(env, path, p);
189 }
190 }
191}
192
193/*
194 * Class: sun_tools_attach_VirtualMachineImpl
195 * Method: close
196 * Signature: (I)V
197 */
198JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
199 (JNIEnv *env, jclass cls, jint fd)
200{
201 int res;
202 shutdown(fd, SHUT_RDWR);
203 RESTARTABLE(close(fd), res);
204}
205
206/*
207 * Class: sun_tools_attach_VirtualMachineImpl
208 * Method: read
209 * Signature: (I[BI)I
210 */
211JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
212 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
213{
214 unsigned char buf[128];
215 size_t len = sizeof(buf);
216 ssize_t n;
217
218 size_t remaining = (size_t)(baLen - off);
219 if (len > remaining) {
220 len = remaining;
221 }
222
223 RESTARTABLE(read(fd, buf, len), n);
224 if (n == -1) {
225 JNU_ThrowIOExceptionWithLastError(env, "read");
226 } else {
227 if (n == 0) {
228 n = -1; // EOF
229 } else {
230 (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
231 }
232 }
233 return n;
234}
235
236/*
237 * Class: sun_tools_attach_VirtualMachineImpl
238 * Method: write
239 * Signature: (I[B)V
240 */
241JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
242 (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
243{
244 size_t remaining = bufLen;
245 do {
246 unsigned char buf[128];
247 size_t len = sizeof(buf);
248 int n;
249
250 if (len > remaining) {
251 len = remaining;
252 }
253 (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
254
255 RESTARTABLE(write(fd, buf, len), n);
256 if (n > 0) {
257 off += n;
258 remaining -= n;
259 } else {
260 JNU_ThrowIOExceptionWithLastError(env, "write");
261 return;
262 }
263
264 } while (remaining > 0);
265}
266