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 }