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 }