1 /*
   2  * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2015 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.  Oracle designates this
   9  * particular file as subject to the "Classpath" exception as provided
  10  * by Oracle in the LICENSE file that accompanied this code.
  11  *
  12  * This code is distributed in the hope that it will be useful, but WITHOUT
  13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15  * version 2 for more details (a copy is included in the LICENSE file that
  16  * accompanied this code).
  17  *
  18  * You should have received a copy of the GNU General Public License version
  19  * 2 along with this work; if not, write to the Free Software Foundation,
  20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  21  *
  22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  23  * or visit www.oracle.com if you need additional information or have any
  24  * questions.
  25  */
  26 
  27 #include "jni.h"
  28 #include "jni_util.h"
  29 #include "jvm.h"
  30 
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <string.h>
  34 #include <errno.h>
  35 #include <unistd.h>
  36 #include <signal.h>
  37 #include <dirent.h>
  38 #include <ctype.h>
  39 #include <sys/types.h>
  40 #include <sys/socket.h>
  41 #include <sys/stat.h>
  42 #include <sys/un.h>
  43 
  44 /*
  45  * Based on 'LinuxVirtualMachine.c'. Non-relevant code has been removed and all
  46  * occurrences of the string "Linux" have been replaced by "Aix".
  47  */
  48 
  49 #include "sun_tools_attach_VirtualMachineImpl.h"
  50 
  51 #define RESTARTABLE(_cmd, _result) do { \
  52   do { \
  53     _result = _cmd; \
  54   } while((_result == -1) && (errno == EINTR)); \
  55 } while(0)
  56 
  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     /* added time out values */
  71     else {
  72         struct timeval tv;
  73         tv.tv_sec = 2 * 60;
  74         tv.tv_usec = 0;
  75 
  76         setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
  77         setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof(tv));
  78     }
  79     return (jint)fd;
  80 }
  81 
  82 /*
  83  * Class:     sun_tools_attach_VirtualMachineImpl
  84  * Method:    connect
  85  * Signature: (ILjava/lang/String;)I
  86  */
  87 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_connect
  88   (JNIEnv *env, jclass cls, jint fd, jstring path)
  89 {
  90     jboolean isCopy;
  91     const char* p = GetStringPlatformChars(env, path, &isCopy);
  92     if (p != NULL) {
  93         struct sockaddr_un addr;
  94         int err = 0;
  95 
  96         memset(&addr, 0, sizeof(addr));
  97         addr.sun_family = AF_UNIX;
  98         /* strncpy is safe because addr.sun_path was zero-initialized before. */
  99         strncpy(addr.sun_path, p, sizeof(addr.sun_path) - 1);
 100         /* We must call bind with the actual socketaddr length. This is obligatory for AS400. */
 101         if (connect(fd, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) {
 102             err = errno;
 103         }
 104 
 105         if (isCopy) {
 106             JNU_ReleaseStringPlatformChars(env, path, p);
 107         }
 108 
 109         /*
 110          * If the connect failed then we throw the appropriate exception
 111          * here (can't throw it before releasing the string as can't call
 112          * JNI with pending exception)
 113          */
 114         if (err != 0) {
 115             if (err == ENOENT) {
 116                 JNU_ThrowByName(env, "java/io/FileNotFoundException", NULL);
 117             } else {
 118                 char* msg = strdup(strerror(err));
 119                 JNU_ThrowIOException(env, msg);
 120                 if (msg != NULL) {
 121                     free(msg);
 122                 }
 123             }
 124         }
 125     }
 126 }
 127 
 128 
 129 /*
 130  * Structure and callback function used to send a QUIT signal to all
 131  * children of a given process
 132  */
 133 typedef struct {
 134     pid_t ppid;
 135 } SendQuitContext;
 136 
 137 static void SendQuitCallback(const pid_t pid, void* user_data) {
 138     SendQuitContext* context = (SendQuitContext*)user_data;
 139     pid_t parent = getParent(pid);
 140     if (parent == context->ppid) {
 141         kill(pid, SIGQUIT);
 142     }
 143 }
 144 
 145 /*
 146  * Class:     sun_tools_attach_VirtualMachineImpl
 147  * Method:    sendQuitTo
 148  * Signature: (I)V
 149  */
 150 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_sendQuitTo
 151   (JNIEnv *env, jclass cls, jint pid)
 152 {
 153     if (kill((pid_t)pid, SIGQUIT)) {
 154         JNU_ThrowIOExceptionWithLastError(env, "kill");
 155     }
 156 }
 157 
 158 /*
 159  * Class:     sun_tools_attach_VirtualMachineImpl
 160  * Method:    checkPermissions
 161  * Signature: (Ljava/lang/String;)V
 162  */
 163 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_checkPermissions
 164   (JNIEnv *env, jclass cls, jstring path)
 165 {
 166     jboolean isCopy;
 167     const char* p = GetStringPlatformChars(env, path, &isCopy);
 168     if (p != NULL) {
 169         struct stat64 sb;
 170         uid_t uid, gid;
 171         int res;
 172         /* added missing initialization of the stat64 buffer */
 173         memset(&sb, 0, sizeof(struct stat64));
 174 
 175         /*
 176          * Check that the path is owned by the effective uid/gid of this
 177          * process. Also check that group/other access is not allowed.
 178          */
 179         uid = geteuid();
 180         gid = getegid();
 181 
 182         res = stat64(p, &sb);
 183         if (res != 0) {
 184             /* save errno */
 185             res = errno;
 186         }
 187 
 188         if (res == 0) {
 189             char msg[100];
 190             jboolean isError = JNI_FALSE;
 191             if (sb.st_uid != uid) {
 192                 jio_snprintf(msg, sizeof(msg)-1,
 193                     "file should be owned by the current user (which is %d) but is owned by %d", uid, sb.st_uid);
 194                 isError = JNI_TRUE;
 195             } else if (sb.st_gid != gid) {
 196                 jio_snprintf(msg, sizeof(msg)-1,
 197                     "file's group should be the current group (which is %d) but the group is %d", gid, sb.st_gid);
 198                 isError = JNI_TRUE;
 199             } else if ((sb.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0) {
 200                 jio_snprintf(msg, sizeof(msg)-1,
 201                     "file should only be readable and writable by the owner but has 0%03o access", sb.st_mode & 0777);
 202                 isError = JNI_TRUE;
 203             }
 204             if (isError) {
 205                 char buf[256];
 206                 jio_snprintf(buf, sizeof(buf)-1, "well-known file %s is not secure: %s", p, msg);
 207                 JNU_ThrowIOException(env, buf);
 208             }
 209         } else {
 210             char* msg = strdup(strerror(res));
 211             JNU_ThrowIOException(env, msg);
 212             if (msg != NULL) {
 213                 free(msg);
 214             }
 215         }
 216 
 217         if (isCopy) {
 218             JNU_ReleaseStringPlatformChars(env, path, p);
 219         }
 220     }
 221 }
 222 
 223 /*
 224  * Class:     sun_tools_attach_VirtualMachineImpl
 225  * Method:    close
 226  * Signature: (I)V
 227  */
 228 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_close
 229   (JNIEnv *env, jclass cls, jint fd)
 230 {
 231     int res;
 232     /* Fixed deadlock when this call of close by the client is not seen by the attach server
 233      * which has accepted the (very short) connection already and is waiting for the request. But read don't get a byte,
 234      * because the close is lost without shutdown.
 235      */
 236     shutdown(fd, 2);
 237     RESTARTABLE(close(fd), res);
 238 }
 239 
 240 /*
 241  * Class:     sun_tools_attach_VirtualMachineImpl
 242  * Method:    read
 243  * Signature: (I[BI)I
 244  */
 245 JNIEXPORT jint JNICALL Java_sun_tools_attach_VirtualMachineImpl_read
 246   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint baLen)
 247 {
 248     unsigned char buf[128];
 249     size_t len = sizeof(buf);
 250     ssize_t n;
 251 
 252     size_t remaining = (size_t)(baLen - off);
 253     if (len > remaining) {
 254         len = remaining;
 255     }
 256 
 257     RESTARTABLE(read(fd, buf, len), n);
 258     if (n == -1) {
 259         JNU_ThrowIOExceptionWithLastError(env, "read");
 260     } else {
 261         if (n == 0) {
 262             n = -1;     // EOF
 263         } else {
 264             (*env)->SetByteArrayRegion(env, ba, off, (jint)n, (jbyte *)(buf));
 265         }
 266     }
 267     return n;
 268 }
 269 
 270 /*
 271  * Class:     sun_tools_attach_VirtualMachineImpl
 272  * Method:    write
 273  * Signature: (I[B)V
 274  */
 275 JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_write
 276   (JNIEnv *env, jclass cls, jint fd, jbyteArray ba, jint off, jint bufLen)
 277 {
 278     size_t remaining = bufLen;
 279     do {
 280         unsigned char buf[128];
 281         size_t len = sizeof(buf);
 282         int n;
 283 
 284         if (len > remaining) {
 285             len = remaining;
 286         }
 287         (*env)->GetByteArrayRegion(env, ba, off, len, (jbyte *)buf);
 288 
 289         RESTARTABLE(write(fd, buf, len), n);
 290         if (n > 0) {
 291             off += n;
 292             remaining -= n;
 293         } else {
 294             JNU_ThrowIOExceptionWithLastError(env, "write");
 295             return;
 296         }
 297 
 298     } while (remaining > 0);
 299 }