Fix swaylock w/shadow on glibc, improve security

Today I learned that GNU flaunts the POSIX standard in yet another
creative way. Additionally, this adds some security improvements,
namely:

- Zeroing out password buffers in the privileged child process
- setuid/setgid after reading /etc/shadow
This commit is contained in:
Drew DeVault 2018-10-06 12:17:36 -04:00
parent ec35e9943e
commit c7776e7804
2 changed files with 30 additions and 0 deletions

View File

@ -26,6 +26,9 @@ else
warning('The swaylock binary must be setuid when compiled without libpam') warning('The swaylock binary must be setuid when compiled without libpam')
warning('You must do this manually post-install: chmod a+s /path/to/swaylock') warning('You must do this manually post-install: chmod a+s /path/to/swaylock')
sources += ['shadow.c'] sources += ['shadow.c']
if crypt.found()
dependencies += [crypt]
endif
endif endif
executable('swaylock', executable('swaylock',

View File

@ -6,9 +6,21 @@
#include <unistd.h> #include <unistd.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "swaylock/swaylock.h" #include "swaylock/swaylock.h"
#ifdef __GLIBC__
// GNU, you damn slimy bastard
#include <crypt.h>
#endif
static int comm[2][2]; static int comm[2][2];
static void clear_buffer(void *buf, size_t bytes) {
volatile char *buffer = buf;
volatile char zero = '\0';
for (size_t i = 0; i < bytes; ++i) {
buffer[i] = zero;
}
}
void run_child(void) { void run_child(void) {
/* This code runs as root */ /* This code runs as root */
struct passwd *pwent = getpwuid(getuid()); struct passwd *pwent = getpwuid(getuid());
@ -25,6 +37,17 @@ void run_child(void) {
} }
encpw = swent->sp_pwdp; encpw = swent->sp_pwdp;
} }
/* We don't need any additional logging here because the parent process will
* also fail here and will handle logging for us. */
if (setgid(getgid()) != 0) {
exit(EXIT_FAILURE);
}
if (setuid(getuid()) != 0) {
exit(EXIT_FAILURE);
}
/* This code does not run as root */
wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name); wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name);
size_t size; size_t size;
@ -60,10 +83,14 @@ void run_child(void) {
result = strcmp(c, encpw) == 0; result = strcmp(c, encpw) == 0;
if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) { if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) {
wlr_log_errno(WLR_ERROR, "failed to write pw check result"); wlr_log_errno(WLR_ERROR, "failed to write pw check result");
clear_buffer(buf, size);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear_buffer(buf, size);
free(buf); free(buf);
} }
clear_buffer(encpw, strlen(encpw));
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }