shell: introduce sdb user daemon for user shell 06/116506/12
authorSooyoung Ha <yoosah.ha@samsung.com>
Sat, 25 Feb 2017 14:45:51 +0000 (23:45 +0900)
committerSooyoung Ha <yoosah.ha@samsung.com>
Wed, 8 Mar 2017 07:54:40 +0000 (16:54 +0900)
To create a shell session via sdb, SDBD open the ptmx and then a pts
device is created. Next, SDBD forks a shell who use the pts opened
previously. BUT, forked shell cannot use the pts because forked shell
has no privilege to read the pts.
So I introduce new sdb user daemon who is only for launching shell.
The sdb user daemon has low privilege, same as user shell, and has few
functions - open ptmx, pass opened ptmx fd to SDBD, and exec shell.

Change-Id: I2332419e4b186d4b18c281a554569735617e9f64
Signed-off-by: Sooyoung Ha <yoosah.ha@samsung.com>
CMakeLists.txt
packaging/sdbd.spec
packaging/sdbd_emulator.service
src/services.c
src/subprocess.c [new file with mode: 0644]

index e16df2d..64c56f9 100644 (file)
@@ -27,51 +27,54 @@ INCLUDE(FindPkgConfig)
 ############################# compiler flags ##################################
 
 SET(SDBD_SRCS
-       src/sdb.c
-       src/fdevent.c
-       src/transport.c
-       src/transport_local.c
-       src/transport_usb.c
-       src/sockets.c
-       src/services.c
-       src/file_sync_service.c
-       src/usb_linux_client.c
-       src/utils.c
-       src/socket_inaddr_any_server.c
-       src/socket_local_client.c
-       src/socket_local_server.c
-       src/socket_loopback_client.c
-       src/socket_loopback_server.c
-       src/socket_network_client.c
-       src/sdktools.c
-       src/strutils.c
-       src/init.c
-       src/fileutils.c
-       src/commandline_sdbd.c
-       src/usb_linux_client.c
-       src/usb_funcfs_client.c
-       src/default_plugin_auth.c
-       src/default_plugin_basic.c
-       src/default_plugin_main.c
-       src/default_plugin_event.c
-       src/default_plugin_appcmd.c
-       src/hashtable.c
-       src/plugin.c
-       src/plugin_encrypt.c
+        src/sdb.c
+        src/fdevent.c
+        src/transport.c
+        src/transport_local.c
+        src/transport_usb.c
+        src/sockets.c
+        src/services.c
+        src/file_sync_service.c
+        src/usb_linux_client.c
+        src/utils.c
+        src/socket_inaddr_any_server.c
+        src/socket_local_client.c
+        src/socket_local_server.c
+        src/socket_loopback_client.c
+        src/socket_loopback_server.c
+        src/socket_network_client.c
+        src/sdktools.c
+        src/strutils.c
+        src/init.c
+        src/fileutils.c
+        src/commandline_sdbd.c
+        src/usb_linux_client.c
+        src/usb_funcfs_client.c
+        src/default_plugin_auth.c
+        src/default_plugin_basic.c
+        src/default_plugin_main.c
+        src/default_plugin_event.c
+        src/default_plugin_appcmd.c
+        src/hashtable.c
+        src/plugin.c
+        src/plugin_encrypt.c
+)
+SET(SDBD_SUBS
+        src/subprocess.c
 )
 
 include(FindPkgConfig)
 
 pkg_check_modules(pkgs REQUIRED
-    libtzplatform-config
-    capi-system-info
-    vconf
-    glib-2.0
-    dbus-1
-    dbus-glib-1
-       dlog
- )
+        libtzplatform-config
+        capi-system-info
+        vconf
+        glib-2.0
+        dbus-1
+        dbus-glib-1
+        dlog
+)
+
 FOREACH(flag ${pkgs_CFLAGS})
         SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
 ENDFOREACH(flag)
@@ -90,54 +93,56 @@ ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64")
 ADD_DEFINITIONS("-DSUPPORT_ENCRYPT")
 
 IF (_ARM_TARGET)
-    ADD_DEFINITIONS("-DANDROID_GADGET=1")
+        ADD_DEFINITIONS("-DANDROID_GADGET=1")
 ENDIF (_ARM_TARGET)
 
 IF(TARGET_ARCH STREQUAL x86)
-    ADD_DEFINITIONS("-DTARGET_ARCH_X86")
+        ADD_DEFINITIONS("-DTARGET_ARCH_X86")
 ELSE()
-    ADD_DEFINITIONS("-DTARGET_ARCH_ARM")
+        ADD_DEFINITIONS("-DTARGET_ARCH_ARM")
 ENDIF()
 
 IF(WEARABLE_PROFILE STREQUAL on)
-    ADD_DEFINITIONS("-D_WEARABLE")
+        ADD_DEFINITIONS("-D_WEARABLE")
 ENDIF()
 
 find_package(Threads REQUIRED)
 
 ADD_EXECUTABLE(sdbd ${SDBD_SRCS})
+ADD_EXECUTABLE(sdbd-user ${SDBD_SUBS})
 TARGET_LINK_LIBRARIES(sdbd -pie -lsmack -lresolv -ldl ${CMAKE_THREAD_LIBS_INIT} ${pkgs_LDFLAGS})
+TARGET_LINK_LIBRARIES(sdbd-user -pie ${CMAKE_THREAD_LIBS_INIT} ${pkgs_LDFLAGS})
 
 set_property(
-       TARGET sdbd
-       PROPERTY COMPILE_DEFINITIONS
-       SDB_HOST=0
-       _DROP_PRIVILEGE
-       _FILE_OFFSET_BITS=64
+        TARGET sdbd
+        PROPERTY COMPILE_DEFINITIONS
+        SDB_HOST=0
+        _DROP_PRIVILEGE
+        _FILE_OFFSET_BITS=64
 )
 
 set_property(
-       TARGET sdbd
-       APPEND PROPERTY COMPILE_DEFINITIONS
-       _XOPEN_SOURCE
-       _GNU_SOURCE
-       HAVE_FORKEXEC
+        TARGET sdbd
+        APPEND PROPERTY COMPILE_DEFINITIONS
+        _XOPEN_SOURCE
+        _GNU_SOURCE
+        HAVE_FORKEXEC
 )
 
 if(USE_FUNCTION_FS)
-       set_property(
-               TARGET sdbd
-               APPEND PROPERTY COMPILE_DEFINITIONS
-               USB_FUNCFS
-       )
+        set_property(
+                TARGET sdbd
+                APPEND PROPERTY COMPILE_DEFINITIONS
+                USB_FUNCFS
+        )
 endif()
 
-install(TARGETS sdbd DESTINATION /usr/sbin)
+install(TARGETS sdbd sdbd-user DESTINATION /usr/sbin)
 install(FILES script/sdbd DESTINATION /etc/init.d)
 
 
 # Optionally build unit tests binary -- could be helpful during further development
 if(BUILD_UNIT_TESTS)
-       enable_testing()
-       add_subdirectory(test)
+        enable_testing()
+        add_subdirectory(test)
 endif()
index 74888df..e595e8c 100644 (file)
@@ -115,12 +115,15 @@ fi
 cp -f /bin/sh /bin/sh-user
 chsmack -a "_" /bin/sh-user
 chsmack -e "User::Shell" /bin/sh-user
+chsmack -a "_" /sbin/sdbd-user
+chsmack -e "User::Shell" /sbin/sdbd-user
 
 %files
 %manifest sdbd.manifest
 %license LICENSE
 %defattr(-,root,root,-)
 %{_prefix}/sbin/sdbd
+%{_prefix}/sbin/sdbd-user
 %{_prefix}/sbin/sdk_launch
 %attr(0755, root, root) %{_sysconfdir}/init.d/sdbd
 %{_unitdir}/sdbd.service
index 6e0ec0f..abd1605 100644 (file)
@@ -8,7 +8,6 @@ After=tmp.mount dbus.service
 User=sdk
 Group=sdk
 Type=forking
-#location of SDBD log file
 Environment=DISPLAY=:0
 PIDFile=/tmp/.sdbd.pid
 RemainAfterExit=yes
index e1a55df..f395fca 100644 (file)
@@ -39,6 +39,8 @@
 #   include <sys/inotify.h>
 #   include "sdktools.h"
 #endif
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include "strutils.h"
 #include "utils.h"
@@ -369,102 +371,142 @@ static int create_service_thread(void (*func)(int, void *), void *cookie)
 }
 
 #if !SDB_HOST
-
-static void redirect_and_exec(int pts, const char *cmd, char * const argv[], char * const envp[])
+/* receive the ptm from child, sdbd-user */
+static ssize_t recv_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
 {
-    dup2(pts, 0);
-    dup2(pts, 1);
-    dup2(pts, 2);
+    struct msghdr msg;
+    struct iovec iov[1];
+    struct cmsghdr *pheader;
+    union {
+        struct cmsghdr cmhdr;
+        char control[CMSG_SPACE(sizeof(int))];
+    } control_un;
+    ssize_t ret;
+
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+
+    iov[0].iov_base = ptr;
+    iov[0].iov_len = nbytes;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    if ((ret = recvmsg(fd, &msg, 0)) <= 0) {
+        return ret;
+    }
 
-    sdb_close(pts);
+    if ((pheader = CMSG_FIRSTHDR(&msg)) != NULL &&
+            pheader->cmsg_len == CMSG_LEN(sizeof(int))) {
+        if (pheader->cmsg_level != SOL_SOCKET) {
+            D("sdb: control level != SOL_SOCKET");
+            exit(-1);
+        }
+        if (pheader->cmsg_type != SCM_RIGHTS) {
+            D("sdb: control type != SCM_RIGHTS");
+            exit(-1);
+        }
+        memcpy(recvfd, CMSG_DATA(pheader), sizeof(int));
+    } else {
+        *recvfd = -1;
+    }
 
-    execve(cmd, argv, envp);
+    return ret;
 }
 
 int create_subprocess(const char *cmd, pid_t *pid, char * const argv[], char * const envp[])
 {
-    char devname[64];
-    int ptm;
-
-    ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
-    if(ptm < 0){
-        D("[ cannot open /dev/ptmx - errno:%d ]\n",errno);
-        return -1;
-    }
-    if (fcntl(ptm, F_SETFD, FD_CLOEXEC) < 0) {
-        D("[ cannot set cloexec to /dev/ptmx - errno:%d ]\n",errno);
-    }
-
-    if(grantpt(ptm) || unlockpt(ptm) ||
-        ptsname_r(ptm, devname, sizeof(devname)) != 0 ){
-        D("[ trouble with /dev/ptmx - errno:%d ]\n", errno);
-        sdb_close(ptm);
-        return -1;
-    }
-
     *pid = fork();
     if(*pid < 0) {
         D("- fork failed: errno:%d -\n", errno);
-        sdb_close(ptm);
         return -1;
     }
 
-    if(*pid == 0){
-        int pts;
-
-        setsid();
-
-        pts = unix_open(devname, O_RDWR);
-        if(pts < 0) {
-            fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
-            exit(-1);
-        }
-
-        sdb_close(ptm);
-
-        // set OOM adjustment to zero
-        {
-            char text[64];
-            //snprintf(text, sizeof text, "/proc/%d/oom_score_adj", getpid());
-            snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid());
-            int fd = sdb_open(text, O_WRONLY);
-            if (fd >= 0) {
-                sdb_write(fd, "0", 1);
-                sdb_close(fd);
-            } else {
-               // FIXME: not supposed to be here
-               D("sdb: unable to open %s due to errno:%d\n", text, errno);
-            }
-        }
-
+    if (*pid == 0) {
         if (should_drop_privileges()) {
             if (argv[2] != NULL && request_validity_to_plugin(PLUGIN_SYNC_CMD_VERIFY_ROOTCMD, argv[2])) {
                 // do nothing
                 D("sdb: executes root commands!!:%s\n", argv[2]);
             } else {
                 if (getuid() != g_sdk_user_id && set_sdk_user_privileges(RESERVE_CAPABILITIES_AFTER_FORK) < 0) {
-                    fprintf(stderr, "failed to set SDK user privileges\n");
+                    D("failed to set SDK user privileges\n");
                     exit(-1);
                 }
             }
         } else {
             set_root_privileges();
         }
-        redirect_and_exec(pts, cmd, argv, envp);
-        fprintf(stderr, "- exec '%s' failed: (errno:%d) -\n",
-                cmd, errno);
+        /* exec sdbduser */
+        execve(cmd, argv, envp);
+        D("- exec '%s' failed: (errno:%d) -\n", cmd, errno);
         exit(-1);
     } else {
         // Don't set child's OOM adjustment to zero.
         // Let the child do it itself, as sometimes the parent starts
         // running before the child has a /proc/pid/oom_adj.
         // """sdb: unable to open /proc/644/oom_adj""" seen in some logs.
+        char tmptext[32];
+        int ptm = -1;
+        struct sockaddr_un addr;
+        int sock;
+        int trycnt = 1;
+
+        /* The child process will open .sdbduser_pid.sock socket.
+           This socket transfers the ptm fd that was opened by child process.
+           You can see related code on subprocess.c file. */
+        snprintf(tmptext, sizeof tmptext, "/tmp/.sdbduser_%d.sock", (int)(*pid));
+        char *sockpath = strdup(tmptext);
+        if (sockpath == NULL) {
+            D("failed to get socket path, %s\n", strerror(errno));
+            return -1;
+        }
+        D("read fd socket is %s\n", sockpath);
+
+        sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+        if (sock == -1) {
+            D("socket error, %s\n", strerror(errno));
+            free(sockpath);
+            return -1;
+        }
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_LOCAL;
+        strcpy(addr.sun_path, sockpath);
+        int slen = offsetof(struct sockaddr_un, sun_path) + strlen(sockpath);
+        while (connect(sock, (struct sockaddr *)&addr, slen) == -1
+                && trycnt < 100) {
+            D("try to connect socket %s, %d times.\n", sockpath, trycnt++);
+            /* sleep maximum 100 times */
+            usleep(10000);
+        }
+        if (trycnt == 100) {
+            D("failed to connect, err: %s\n", strerror(errno));
+            free(sockpath);
+            return -1;
+        }
+
+        char c;
+        if (recv_fd(sock, &c, 1, &ptm) == -1) {
+            D("recv_fd error, %s\n", strerror(errno));
+            free(sockpath);
+            return -1;
+        } else {
+            D("got ptm fd from child, fd: %d\n", ptm);
+        }
+
+        if (sdb_close(sock) == -1) {
+            D("close sock error, %s\n", strerror(errno));
+        }
+        free(sockpath);
+
+        D("getting child's ptm successed.\n");
         return ptm;
     }
 }
 #endif  /* !SDB_HOST */
 
-#define SHELL_COMMAND "/bin/sh-user"
+#define SHELL_COMMAND "/usr/sbin/sdbd-user"
 #define LOGIN_COMMAND "/bin/login"
 #define SUPER_USER    "root"
 #define LOGIN_CONFIG  "/etc/login.defs"
@@ -478,6 +520,7 @@ static void subproc_waiter_service(int fd, void *cookie)
     for (;;) {
         int status;
         pid_t p = waitpid(pid, &status, 0);
+        D("entered fd=%d, post waitpid(pid=%d)\n", fd, p);
         if (p == pid) {
             D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
             if (WIFEXITED(status)) {
diff --git a/src/subprocess.c b/src/subprocess.c
new file mode 100644 (file)
index 0000000..c5d4bbd
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include "sysdeps.h"
+#include "sdb.h"
+
+#define SHELL_COMMAND "/bin/sh-user"
+
+/* to send ptm fd to sdbd main */
+static ssize_t send_fd(int fd, void *ptr, size_t nbytes, int sendfd)
+{
+    struct msghdr msg;
+    struct iovec iov[1];
+    struct cmsghdr *pheader;
+    union {
+        struct cmsghdr cmhdr;
+        char control[CMSG_SPACE(sizeof(int))];
+    } control_un;
+
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+
+    pheader = CMSG_FIRSTHDR(&msg);
+    pheader->cmsg_len = CMSG_LEN(sizeof(int));
+    pheader->cmsg_level = SOL_SOCKET;
+    pheader->cmsg_type = SCM_RIGHTS;
+    memcpy(CMSG_DATA(pheader), &sendfd, sizeof(int));
+
+    msg.msg_name = NULL;
+    msg.msg_namelen = 0;
+
+    iov[0].iov_base = ptr;
+    iov[0].iov_len = nbytes;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    return sendmsg(fd, &msg, 0);
+}
+
+static void redirect_and_exec(int pts, const char *cmd, char * const argv[], char * const envp[])
+{
+    dup2(pts, 0);
+    dup2(pts, 1);
+    dup2(pts, 2);
+
+    sdb_close(pts);
+
+    execve(cmd, argv, envp);
+}
+
+int main (int argc, char **argv, char **envp)
+{
+    char devname[16];
+    int ptm;
+    pid_t pid;
+    int ret = -1;
+
+    ptm = unix_open("/dev/ptmx", O_RDWR | O_CLOEXEC);
+    if(ptm < 0){
+        fprintf(stderr, "sdbu cannot open /dev/ptmx, errno: %d\n",errno);
+        return ret;
+    }
+
+    if(grantpt(ptm) || unlockpt(ptm) ||
+        ptsname_r(ptm, devname, sizeof(devname)) != 0 ){
+        fprintf(stderr, "sdbu trouble with /dev/ptmx, errno: %d\n", errno);
+        sdb_close(ptm);
+        return ret;
+    }
+    fprintf(stderr, "sdbu ptm opening success\n");
+
+    pid = fork();
+    if(pid < 0) {
+        fprintf(stderr, "sdbu fork failed, errno: %d\n", errno);
+        sdb_close(ptm);
+        return ret;
+    }
+
+    /* sdbd-user's child.
+       This just open pts and exec sh-user */
+    if (pid == 0) {
+        int pts;
+        setsid();
+        pts = unix_open(devname, O_RDWR | O_CLOEXEC);
+        if(pts < 0) {
+            fprintf(stderr, "sdbu: child failed to open pseudo-term slave %s\n", devname);
+            exit(-1);
+        }
+        fprintf(stderr, "sdbu: child open pts %s\n", devname);
+
+        sdb_close(ptm);
+
+        // set OOM adjustment to zero
+        {
+            char tmptext[64];
+            snprintf(tmptext, sizeof tmptext, "/proc/%d/oom_adj", getpid());
+            int fd = sdb_open(tmptext, O_WRONLY | O_CLOEXEC);
+            if (fd >= 0) {
+                sdb_write(fd, "0", 1);
+                sdb_close(fd);
+            } else {
+               fprintf(stderr, "sdbu: child unable to open %s due to errno:%d\n", tmptext, errno);
+            }
+        }
+
+        /* argv[0] should be /bin/sh-user. original data is /usr/sbin/sdbd-user  */
+        argv[0] = SHELL_COMMAND;
+        redirect_and_exec(pts, SHELL_COMMAND, argv, envp);
+        /* if exec error */
+        fprintf(stderr, "sdbu: child exec %s failed, errno: %d\n", SHELL_COMMAND, errno);
+        exit(-1);
+    } else {
+        /* sdbd-user process.
+           This process open the ptm and unix socket to send ptm. */
+        /* socket create and open and bind, listen, accept and send fd here. */
+        char tmptext[32];
+        struct sockaddr_un addr;
+        int sock, s;
+        char c;
+        pid_t mypid = getpid();
+
+        snprintf(tmptext, sizeof tmptext, "/tmp/.sdbduser_%d.sock", (int)mypid);
+
+        char *sockpath = strdup(tmptext);
+        if (sockpath == NULL) {
+            fprintf(stderr, "sdbu socket path error, %s\n", strerror(errno));
+            exit(-1);
+        }
+
+        sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+        if (sock == -1) {
+            fprintf(stderr, "sdbu socket error, %s\n", strerror(errno));
+        }
+        memset(&addr, 0, sizeof(addr));
+        addr.sun_family = AF_LOCAL;
+        strcpy(addr.sun_path, sockpath);
+        int slen = offsetof(struct sockaddr_un, sun_path) + strlen(sockpath);
+        if (bind(sock, (struct sockaddr *)&addr, slen) == -1) {
+            fprintf(stderr, "sdbu socket bind error, %s\n", strerror(errno));
+        }
+        if (listen(sock, 5) == -1) {
+            fprintf(stderr, "sdbu listen error, %s\n", strerror(errno));
+        }
+        if ((s = sdb_socket_accept(sock, NULL, 0)) == -1) {
+            fprintf(stderr, "sdbu accept error, %s\n", strerror(errno));
+        }
+
+        /* send ptm fd to sdbd */
+        if (send_fd(s, &c, 1, ptm) != 0) {
+            fprintf(stderr, "sdbu send fd error, %s\n", strerror(errno));
+        }
+
+        sdb_close(s);
+        sdb_close(sock);
+        sdb_unlink(sockpath);
+        free(sockpath);
+        /* socket end */
+
+        /* simply wait child */
+        int status, ret;
+        ret = wait(&status);
+        fprintf(stderr, "sdbu pid %d has ", ret);
+        if (WIFEXITED(status)) {
+            fprintf(stderr, "terminated normally %d.\n", WEXITSTATUS(status));
+        } else if (WIFSIGNALED(status)) {
+            fprintf(stderr, "signaled %d.\n", WTERMSIG(status));
+        } else if (WIFSTOPPED(status)) {
+            fprintf(stderr, "stopped.\n");
+        } else if (WIFCONTINUED(status)) {
+            fprintf(stderr, "continued.\n");
+        } else {
+            fprintf(stderr, "terminated abnormally.\n");
+        }
+        return 0;
+    }
+}