diff options
author | Werner Fink <werner@suse.de> | 2020-11-16 13:21:53 +0100 |
---|---|---|
committer | Werner Fink <werner@suse.de> | 2020-11-25 16:37:05 +0100 |
commit | 26d35af198da01220ba4f7a1b987f17012476c00 (patch) | |
tree | 452f70088363702abfef8bd2dd61072b5e33aca9 | |
parent | Fixed compiler warnings (diff) | |
download | haveged-26d35af198da01220ba4f7a1b987f17012476c00.tar.xz haveged-26d35af198da01220ba4f7a1b987f17012476c00.zip |
Fix error handling as well as add new command
for closing the communication socket. As well as get
error messages back from running instance to client process.
Signed-off-by: Werner Fink <werner@suse.de>
-rw-r--r-- | man/haveged.8 | 6 | ||||
-rw-r--r-- | src/havegecmd.c | 200 | ||||
-rw-r--r-- | src/havegecmd.h | 17 | ||||
-rw-r--r-- | src/haveged.c | 82 | ||||
-rw-r--r-- | src/haveged.h | 5 |
5 files changed, 226 insertions, 84 deletions
diff --git a/man/haveged.8 b/man/haveged.8 index 75bc5a2..edbc611 100644 --- a/man/haveged.8 +++ b/man/haveged.8 @@ -34,7 +34,11 @@ Set collection buffer size to nnn KW. Default is 128KW (or 512KB). -c cmd, --command=cmd Switch to command mode and send a command to an already running .B haveged -process or daemon. Currently the only knows command is +process or daemon. Currently the only known commands are +.I close +to close the current communication socket of the running +.B haveged +process as well as .IR root = <new_root> where .I <new_root> 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 diff --git a/src/havegecmd.h b/src/havegecmd.h index 997c5d1..a9f2b9a 100644 --- a/src/havegecmd.h +++ b/src/havegecmd.h @@ -34,6 +34,12 @@ extern "C" { #define HAVEGED_SOCKET_PATH "\0/sys/entropy/haveged" #define MAGIC_CHROOT 'R' +#define MAGIC_CLOSE 'X' +#define MAGIC_PATH 'P' + +#define ASCII_ACK "\x6" /* ASCII acknowledge */ +#define ASCII_NAK "\x15" /* ASCII negative acknowledge */ +#define ASCII_STX "\x2" /* ASCII start of text */ #ifndef SOCK_CLOEXEC #define SOCK_CLOEXEC 0 @@ -74,10 +80,21 @@ ssize_t safein(int, void *, size_t); void safeout(int, const void *, size_t); /** + * Send outgoing unsigned integer to socket + */ +void send_uinteger(int, uint32_t); + +/** + * Receive incomming unsigned integer from socket + */ +int receive_uinteger(int, uint32_t *); + +/** * Socket file descriptor used for communication */ extern int socket_fd; +extern int first_byte; #ifdef __cplusplus } diff --git a/src/haveged.c b/src/haveged.c index 733d0da..4440a45 100644 --- a/src/haveged.c +++ b/src/haveged.c @@ -19,6 +19,7 @@ ** along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "config.h" +#include <sys/auxv.h> #include <stdlib.h> #include <stdio.h> #include <getopt.h> @@ -118,7 +119,6 @@ static void set_watermark(int level); static void anchor_info(H_PTR h); static int get_runsize(unsigned int *bufct, unsigned int *bufrem, char *bp); static char *ppSize(char *buffer, double sz); -static void print_msg(const char *format, ...); static void run_app(H_PTR handle, H_UINT bufct, H_UINT bufres); static void show_meterInfo(H_UINT id, H_UINT event); @@ -135,6 +135,8 @@ int main(int argc, char **argv) { volatile char *path = strdup(argv[0]); volatile char *arg0 = argv[0]; + if (path[0] != '/') + path = (char*)getauxval(AT_EXECFN); static const char* cmds[] = { "b", "buffer", "1", SETTINGR("Buffer size [KW], default: ",COLLECT_BUFSIZE), "d", "data", "1", SETTINGR("Data cache size [KB], with fallback to: ", GENERIC_DCACHE ), @@ -188,6 +190,7 @@ int main(int argc, char **argv) params->setup |= MULTI_CORE; #endif + first_byte = arg0[0]; if (access("/etc/initrd-release", F_OK) >= 0) { arg0[0] = '@'; path[0] = '/'; @@ -339,8 +342,10 @@ int main(int argc, char **argv) #ifndef NO_COMMAND_MODE if (params->setup & CMD_MODE) { int ret = 0, len; - char *ptr, message[PATH_MAX+5], answer[2], cmd[2]; + uint32_t size; + char *ptr, answer[6], cmd[2]; fd_set read_fd; + sigset_t block; socket_fd = cmd_connect(params); if (socket_fd < 0) { @@ -357,14 +362,15 @@ int main(int argc, char **argv) char *root; case MAGIC_CHROOT: root = optarg; - len = (int)strlen(root); - ret = snprintf(message, sizeof(message), "%c\002%c%s%n", cmd[0], (int)(strlen(root) + 1), root, &len); - if (ret < 0 || (unsigned) ret >= sizeof(message)) { - fprintf(stderr, "%s: can not store message\n", params->daemon); - break; - } - ptr = &message[0]; - len += 1; + size = (uint32_t)strlen(root)+1; + cmd[1] = '\002'; + safeout(socket_fd, &cmd[0], 2); + send_uinteger(socket_fd, size); + safeout(socket_fd, root, size); + break; + case MAGIC_CLOSE: + ptr = &cmd[0]; + len = (int)strlen(cmd)+1; safeout(socket_fd, ptr, len); break; case '?': @@ -374,13 +380,15 @@ int main(int argc, char **argv) } answer[0] = '\0'; ptr = &answer[0]; - len = sizeof(answer); + + sigemptyset(&block); + sigaddset(&block, SIGPIPE); FD_ZERO(&read_fd); FD_SET(socket_fd, &read_fd); do { - struct timeval two = {2, 0}; + struct timeval two = {6, 0}; ret = select(socket_fd+1, &read_fd, NULL, NULL, &two); if (ret >= 0) break; if (errno != EINTR) @@ -388,15 +396,37 @@ int main(int argc, char **argv) } while (1); - ret = safein(socket_fd, ptr, len); - close(socket_fd); + ret = safein(socket_fd, ptr, 1); if (ret < 0) goto err; - if (answer[0] != '\x6') - ret = -1; - else - ret = 0; + switch (answer[0]) { + case '\002': { + char *msg; + ret = receive_uinteger(socket_fd, &size); + if (ret < 0) + goto err; + msg = calloc(size, sizeof(char)); + if (!msg) + error_exit("can not allocate memory for message from UNIX socket: %s", + strerror(errno)); + ret = safein(socket_fd, msg, size); + if (ret < 0) + goto err; + fprintf(stderr, "%s: %s", params->daemon, msg); + free(msg); + ret = -1; + } + break; + case '\x6': + ret = 0; + break; + case '\x15': + default: + ret = -1; + break; + } err: + close(socket_fd); return ret; } else { @@ -404,13 +434,13 @@ int main(int argc, char **argv) if (socket_fd >= 0) fprintf(stderr, "%s: command socket is listening at fd %d\n", params->daemon, socket_fd); else { - if (socket_fd == -2) { - fprintf(stderr, "%s: command socket already in use\n", params->daemon); - fprintf(stderr, "%s: please check if there is another instance of haveged running\n", params->daemon); - fprintf(stderr, "%s: disabling command mode for this instance\n", params->daemon); - } else { - fprintf(stderr, "%s: can not initialize command socket: %s\n", params->daemon, strerror(errno)); - } + if (socket_fd == -2) { + fprintf(stderr, "%s: command socket already in use\n", params->daemon); + error_exit("%s: please check if there is another instance of haveged running\n", params->daemon); + } else { + fprintf(stderr, "%s: can not initialize command socket: %s\n", params->daemon, strerror(errno)); + fprintf(stderr, "%s: disabling command mode for this instance\n", params->daemon); + } } } #endif @@ -834,7 +864,7 @@ static char *ppSize( /* RETURN: the formatted size */ /** * Execution notices - to stderr or syslog */ -static void print_msg( /* RETURN: nothing */ +void print_msg( /* RETURN: nothing */ const char *format, /* IN: format string */ ...) /* IN: args */ { diff --git a/src/haveged.h b/src/haveged.h index 9b1efaf..40e07a0 100644 --- a/src/haveged.h +++ b/src/haveged.h @@ -92,4 +92,9 @@ typedef struct { */ void error_exit(const char *, ...); +/** + * Execution notices - to stderr or syslog + */ +void print_msg(const char *, ...); + #endif |