From: Sooyoung Ha Date: Sat, 25 Feb 2017 14:45:51 +0000 (+0900) Subject: shell: introduce sdb user daemon for user shell X-Git-Tag: accepted/tizen/3.0/common/20170309.110602~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F06%2F116506%2F12;p=sdk%2Ftarget%2Fsdbd.git shell: introduce sdb user daemon for user shell 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 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index e16df2d..64c56f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/packaging/sdbd.spec b/packaging/sdbd.spec index 74888df..e595e8c 100644 --- a/packaging/sdbd.spec +++ b/packaging/sdbd.spec @@ -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 diff --git a/packaging/sdbd_emulator.service b/packaging/sdbd_emulator.service index 6e0ec0f..abd1605 100644 --- a/packaging/sdbd_emulator.service +++ b/packaging/sdbd_emulator.service @@ -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 diff --git a/src/services.c b/src/services.c index e1a55df..f395fca 100644 --- a/src/services.c +++ b/src/services.c @@ -39,6 +39,8 @@ # include # include "sdktools.h" #endif +#include +#include #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 index 0000000..c5d4bbd --- /dev/null +++ b/src/subprocess.c @@ -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 +#include +#include +#include +#include + +#include +#include +#include + +#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; + } +}