diff options
Diffstat (limited to 'src/havegecmd.c')
-rw-r--r-- | src/havegecmd.c | 200 |
1 files changed, 143 insertions, 57 deletions
diff --git a/src/havegecmd.c b/src/havegecmd.c index 18ff322..0a324ed 100644 --- a/src/havegecmd.c +++ b/src/havegecmd.c @@ -26,6 +26,7 @@ #ifndef NO_COMMAND_MODE #include <errno.h> +#include <fcntl.h> #include <limits.h> #include <stdarg.h> #include <stdio.h> @@ -35,6 +36,7 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/un.h> #include <unistd.h> @@ -49,45 +51,60 @@ struct ucred #include "havegecmd.h" +int first_byte; int socket_fd; +static char errmsg[1024]; -static void new_root( /* RETURN: nothing */ +static int new_root( /* RETURN: status */ const char *root, /* IN: path of the new root file system */ const volatile char *path, /* IN: path of the haveged executable */ char *const argv[], /* IN: arguments for the haveged process */ struct pparams *params) /* IN: input params */ { int ret; + struct stat st; + const char *realtive = (const char*)&path[0]; - fprintf(stderr, "%s: restart in new root: %s\n", params->daemon, root); ret = chdir(root); if (ret < 0) { - if (errno != ENOENT) - error_exit("can't change to working directory : %s", root); - else - fprintf(stderr, "%s: can't change to working directory : %s\n", params->daemon, root); + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change to working directory %s: %s\n", + root, strerror(errno)); + goto err; + } + if (path[0] == '/') + realtive++; + ret = fstatat(AT_FDCWD, realtive, &st, 0); + if (ret < 0) { + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't restart %s: %s\n", + path, strerror(errno)); + goto err; } ret = chroot("."); if (ret < 0) { - if (errno != ENOENT) - error_exit("can't change root directory"); - else - fprintf(stderr, "%s: can't change root directory\n", params->daemon); + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change root directory %s\n", + strerror(errno)); + goto err; } ret = chdir("/"); if (ret < 0) { - if (errno != ENOENT) - error_exit("can't change to working directory /"); - else - fprintf(stderr, "%s: can't change to working directory /\n", params->daemon); + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't change to working directory / : %s\n", + strerror(errno)); + goto err; } ret = execv((const char *)path, argv); if (ret < 0) { - if (errno != ENOENT) - error_exit("can't restart %s", path); - else - fprintf(stderr, "%s: can't restart %s\n", params->daemon, path); - } + snprintf(&errmsg[0], sizeof(errmsg)-1, + "can't restart %s: %s\n", + path, strerror(errno)); + } +err: + if (ret < 0) + print_msg("%s", errmsg); + return ret; } /** @@ -105,7 +122,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */ fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) { - fprintf(stderr, "%s: can not open UNIX socket\n", params->daemon); + print_msg("%s: can not open UNIX socket\n", params->daemon); goto err; } @@ -113,7 +130,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */ if (ret < 0) { close(fd); fd = -1; - fprintf(stderr, "%s: can not set option for UNIX socket\n", params->daemon); + print_msg("%s: can not set option for UNIX socket\n", params->daemon); goto err; } @@ -122,7 +139,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */ close(fd); fd = -1; if (errno != EADDRINUSE) - fprintf(stderr, "%s: can not bind a name to UNIX socket\n", params->daemon); + print_msg("%s: can not bind a name to UNIX socket\n", params->daemon); else fd = -2; goto err; @@ -132,7 +149,7 @@ int cmd_listen( /* RETURN: UNIX socket file descriptor */ if (ret < 0) { close(fd); fd = -1; - fprintf(stderr, "%s: can not listen on UNIX socket\n", params->daemon); + print_msg("%s: can not listen on UNIX socket\n", params->daemon); goto err; } err: @@ -154,13 +171,13 @@ int cmd_connect( /* RETURN: UNIX socket file descriptor */ fd = socket(PF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) { - fprintf(stderr, "%s: can not open UNIX socket\n", params->daemon); + print_msg("%s: can not open UNIX socket\n", params->daemon); goto err; } ret = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t)sizeof(one)); if (ret < 0) { - fprintf(stderr, "%s: can not set option for UNIX socket\n", params->daemon); + print_msg("%s: can not set option for UNIX socket\n", params->daemon); close(fd); fd = -1; goto err; @@ -169,7 +186,7 @@ int cmd_connect( /* RETURN: UNIX socket file descriptor */ ret = connect(fd, (struct sockaddr *)&su, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(su.sun_path+1)); if (ret < 0) { if (errno != ECONNREFUSED) - fprintf(stderr, "%s: can not connect on UNIX socket\n", params->daemon); + print_msg("%s: can not connect on UNIX socket\n", params->daemon); close(fd); fd = -1; goto err; @@ -191,6 +208,7 @@ int getcmd( /* RETURN: success or error */ const char* opt; } cmds[] = { { "root=", MAGIC_CHROOT, 1, NULL }, /* New root */ + { "close", MAGIC_CLOSE, 0, NULL }, /* Close socket */ {0} }, *cmd = cmds; int ret = -1; @@ -235,39 +253,52 @@ int socket_handler( /* RETURN: closed file descriptor */ int ret = -1, len; if (fd < 0) { - fprintf(stderr, "%s: no connection jet\n", params->daemon); + print_msg("%s: no connection jet\n", params->daemon); } ptr = &magic[0]; len = sizeof(magic); ret = safein(fd, ptr, len); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } - if (magic[1] == '\002') { /* read argument provided */ - unsigned char alen; + if (magic[1] == '\002') { /* ASCII start of text: read argument provided */ + uint32_t alen; - ret = safein(fd, &alen, sizeof(unsigned char)); + ret = receive_uinteger(fd, &alen); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } optarg = calloc(alen, sizeof(char)); - if (!optarg) - error_exit("can not allocate memory for message from UNIX socket"); - + if (!optarg) { + print_msg("can not allocate memory for message from UNIX socket"); + goto out; + } ptr = (unsigned char*)optarg; - len = alen; - ret = safein(fd, ptr, len); + + ret = safein(fd, ptr, alen); + if (ret < 0) { + print_msg("%s: can not read from UNIX socket\n", params->daemon); + goto out; + } } clen = sizeof(struct ucred); ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &clen); if (ret < 0) { - fprintf(stderr, "%s: can not get credentials from UNIX socket part1\n", params->daemon); + print_msg("%s: can not get credentials from UNIX socket part1\n", params->daemon); goto out; } if (clen != sizeof(struct ucred)) { - fprintf(stderr, "%s: can not get credentials from UNIX socket part2\n", params->daemon); + print_msg("%s: can not get credentials from UNIX socket part2\n", params->daemon); goto out; } if (cred.uid != 0) { - enqry = "\x15"; + enqry = ASCII_NAK; ptr = (unsigned char *)enqry; len = (int)strlen(enqry)+1; @@ -276,19 +307,39 @@ int socket_handler( /* RETURN: closed file descriptor */ switch (magic[0]) { case MAGIC_CHROOT: - enqry = "\x6"; + enqry = ASCII_ACK; + + ret = new_root(optarg, path, argv, params); + if (ret < 0) { + uint32_t size = strlen(errmsg); + safeout(fd, ASCII_STX, strlen(ASCII_STX)); + send_uinteger(fd, size); + safeout(fd, errmsg, size+1); + break; + } + + ptr = (unsigned char *)enqry; + len = (int)strlen(enqry); + safeout(fd, ptr, len); + + break; + case MAGIC_CLOSE: + enqry = ASCII_ACK; + + close(socket_fd); + socket_fd = -1; ptr = (unsigned char *)enqry; - len = (int)strlen(enqry)+1; + len = (int)strlen(enqry); safeout(fd, ptr, len); + argv[0][0] = first_byte; - new_root(optarg, path, argv, params); break; default: - enqry = "\x15"; + enqry = ASCII_NAK; ptr = (unsigned char *)enqry; - len = (int)strlen(enqry)+1; + len = (int)strlen(enqry); safeout(fd, ptr, len); break; } @@ -308,31 +359,30 @@ out: ssize_t safein( /* RETURN: read bytes */ int fd, /* IN: file descriptor */ void *ptr, /* OUT: pointer to buffer */ - size_t sz) /* IN: size of buffer */ + size_t len) /* IN: size of buffer */ { - int saveerr = errno, t; + int saveerr = errno, avail; ssize_t ret = 0; - size_t len; - if (sz > SSIZE_MAX) - sz = SSIZE_MAX; + if (len > SSIZE_MAX) + len = SSIZE_MAX; - t = 0; - if ((ioctl(fd, FIONREAD, &t) < 0) || (t <= 0)) + ret = ioctl(fd, FIONREAD, &avail); + if (ret < 0 || avail <=0) goto out; - len = (size_t)t; - if (len > sz) - len = sz; + if (len > avail) + len = avail; do { - ssize_t p = recv(fd, ptr, len, MSG_DONTWAIT); + errno = saveerr; + ssize_t p = recv(fd, ptr, len, 0 /* MSG_DONTWAIT */); if (p < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) break; - error_exit("Unable to read from socket: %d", socket_fd); + print_msg("Unable to read from socket %d: %s", socket_fd, strerror(errno)); } ptr = (char *) ptr + p; ret += p; @@ -340,7 +390,6 @@ ssize_t safein( /* RETURN: read bytes */ } while (len > 0); out: - errno = saveerr; return ret; } @@ -361,7 +410,7 @@ void safeout( /* RETURN: nothing */ continue; if (errno == EPIPE || errno == EAGAIN || errno == EWOULDBLOCK) break; - error_exit("Unable to write to socket: %d", fd); + print_msg("Unable to write to socket %d: %s", fd, strerror(errno)); } ptr = (char *) ptr + p; len -= p; @@ -371,4 +420,41 @@ void safeout( /* RETURN: nothing */ errno = saveerr; } +/** + * Send outgoing unsigned integer to socket + */ +void send_uinteger( /* RETURN: nothing */ + int fd, /* IN: file descriptor */ + uint32_t value) /* IN: 32 bit unsigned integer */ +{ + uint8_t buffer[4]; + + buffer[0] = (uint8_t)((value >> 24) & 0xFF); + buffer[1] = (uint8_t)((value >> 16) & 0xFF); + buffer[2] = (uint8_t)((value >> 8) & 0xFF); + buffer[3] = (uint8_t)((value >> 0) & 0xFF); + + safeout(fd, buffer, 4 * sizeof(uint8_t)); +} + +/** + * Receive incomming unsigned integer from socket + */ +int receive_uinteger( /* RETURN: status */ + int fd, /* IN: file descriptor */ + uint32_t *value) /* OUT: 32 bit unsigned integer */ +{ + uint8_t buffer[4]; + + if (safein(fd, buffer, 4 * sizeof(uint8_t)) < 0) + return -1; + + *value = (((uint32_t)buffer[0]) << 24) | + (((uint32_t)buffer[1]) << 16) | + (((uint32_t)buffer[2]) << 8) | + (((uint32_t)buffer[3]) << 0); + + return 0; +} + #endif |