Skip to content

Commit

Permalink
Correct the UACC wtmp path (#17416)
Browse files Browse the repository at this point in the history
  • Loading branch information
jakule committed Dec 14, 2022
1 parent 3fe7a99 commit e954110
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 50 deletions.
68 changes: 33 additions & 35 deletions lib/srv/uacc/uacc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,6 @@ limitations under the License.
#include <stdlib.h>
#include <limits.h>

// Sometimes the _UTMP_PATH and _WTMP_PATH macros from glibc are bad, this seems to depend on distro.
// I asked around on IRC, no one really knows why. I suspect it's another
// archaic remnant of old Unix days and that a cleanup is long overdue.
//
// In the meantime, we just try to resolve from these paths instead.
#define UACC_UTMP_PATH "/var/run/utmp"
#define UACC_WTMP_PATH "/var/run/wtmp"

int UACC_UTMP_MISSING_PERMISSIONS = 1;
int UACC_UTMP_WRITE_ERROR = 2;
int UACC_UTMP_READ_ERROR = 3;
Expand All @@ -60,21 +52,6 @@ char* UACC_PATH_ERR;
// This decision is one of the origins of `strncpy` which has the special property that it will not write a NUL terminator if the
// source string excluding the NUL terminator is of equal or greater length in comparison to the limit parameter.

// get_absolute_path_with_fallback attempts to resolve the `supplied_path` path. If `supplied` is null it will
// resolve the `fallback_path` path. The resolved path is stored in `buffer` and will at most be
// `PATH_MAX` long including the null terminator.
static char* get_absolute_path_with_fallback(char* buffer, const char* supplied_path, const char* fallback_path) {
const char* path;

if (supplied_path != NULL) {
path = supplied_path;
} else {
path = fallback_path;
}

return realpath(path, buffer);
}

static int check_abs_path_err(const char* buffer) {
// check for errors
if (buffer == NULL) {
Expand Down Expand Up @@ -108,28 +85,39 @@ static int max_len_tty_name() {

// Low level C function to add a new USER_PROCESS entry to the database.
// This function does not perform any argument validation.
static int uacc_add_utmp_entry(const char *utmp_path, const char *wtmp_path, const char *username, const char *hostname, const int32_t remote_addr_v6[4], const char *tty_name, const char *id, int32_t tv_sec, int32_t tv_usec) {
static int uacc_add_utmp_entry(const char *utmp_path, const char *wtmp_path, const char *username,
const char *hostname, const int32_t remote_addr_v6[4], const char *tty_name, const char *id,
int32_t tv_sec, int32_t tv_usec) {

if (utmp_path == NULL || wtmp_path == NULL) {
// Return open failed error if any of the provided paths is NULL.
return UACC_UTMP_FAILED_OPEN;
}

UACC_PATH_ERR = NULL;
char resolved_utmp_buffer[PATH_MAX];
const char* file = get_absolute_path_with_fallback(&resolved_utmp_buffer[0], utmp_path, UACC_UTMP_PATH);
const char* file = realpath(utmp_path, &resolved_utmp_buffer[0]);

int status = check_abs_path_err(file);
if (status != 0) {
return status;
}
if (utmpname(file) < 0) {
return UACC_UTMP_FAILED_TO_SELECT_FILE;
}
struct utmp entry;
entry.ut_type = USER_PROCESS;
struct utmp entry = {
.ut_type = USER_PROCESS,
.ut_pid = getpid(),
.ut_session = getsid(0),
.ut_tv.tv_sec = tv_sec,
.ut_tv.tv_usec = tv_usec
};
strncpy((char*) &entry.ut_line, tty_name, UT_LINESIZE);
strncpy((char*) &entry.ut_id, id, sizeof(entry.ut_id));
entry.ut_pid = getpid();
strncpy((char*) &entry.ut_host, hostname, sizeof(entry.ut_host));
strncpy((char*) &entry.ut_user, username, sizeof(entry.ut_user));
entry.ut_session = getsid(0);
entry.ut_tv.tv_sec = tv_sec;
entry.ut_tv.tv_usec = tv_usec;
memcpy(&entry.ut_addr_v6, &remote_addr_v6, sizeof(int32_t) * 4);

errno = 0;
setutent();
if (errno > 0) {
Expand All @@ -142,7 +130,7 @@ static int uacc_add_utmp_entry(const char *utmp_path, const char *wtmp_path, con
}
endutent();
char resolved_wtmp_buffer[PATH_MAX];
const char* wtmp_file = get_absolute_path_with_fallback(&resolved_wtmp_buffer[0], wtmp_path, UACC_WTMP_PATH);
const char* wtmp_file = realpath(wtmp_path, &resolved_wtmp_buffer[0]);
status = check_abs_path_err(wtmp_file);
if (status != 0) {
return status;
Expand All @@ -154,9 +142,14 @@ static int uacc_add_utmp_entry(const char *utmp_path, const char *wtmp_path, con
// Low level C function to mark a database entry as DEAD_PROCESS.
// This function does not perform string argument validation.
static int uacc_mark_utmp_entry_dead(const char *utmp_path, const char *wtmp_path, const char *tty_name, int32_t tv_sec, int32_t tv_usec) {
if (utmp_path == NULL || wtmp_path == NULL) {
// Return open failed error if any of the provided paths is NULL.
return UACC_UTMP_FAILED_OPEN;
}

UACC_PATH_ERR = NULL;
char resolved_utmp_buffer[PATH_MAX];
const char* file = get_absolute_path_with_fallback(&resolved_utmp_buffer[0], utmp_path, UACC_UTMP_PATH);
const char* file = realpath(utmp_path, &resolved_utmp_buffer[0]);
int status = check_abs_path_err(file);
if (status != 0) {
return status;
Expand Down Expand Up @@ -197,7 +190,7 @@ static int uacc_mark_utmp_entry_dead(const char *utmp_path, const char *wtmp_pat
}
endutent();
char resolved_wtmp_buffer[PATH_MAX];
const char* wtmp_file = get_absolute_path_with_fallback(&resolved_wtmp_buffer[0], wtmp_path, UACC_WTMP_PATH);
const char* wtmp_file = realpath(wtmp_path, &resolved_wtmp_buffer[0]);
status = check_abs_path_err(wtmp_file);
if (status != 0) {
return status;
Expand All @@ -209,9 +202,14 @@ static int uacc_mark_utmp_entry_dead(const char *utmp_path, const char *wtmp_pat
// Low level C function to check the database for an entry for a given user.
// This function does not perform string argument validation.
static int uacc_has_entry_with_user(const char *utmp_path, const char *user) {
if (utmp_path == NULL) {
// Return open failed error if any of the provided paths is NULL.
return UACC_UTMP_FAILED_OPEN;
}

UACC_PATH_ERR = NULL;
char resolved_utmp_buffer[PATH_MAX];
const char* file = get_absolute_path_with_fallback(&resolved_utmp_buffer[0], utmp_path, UACC_UTMP_PATH);
const char* file = realpath(utmp_path, &resolved_utmp_buffer[0]);
int status = check_abs_path_err(file);
if (status != 0) {
return status;
Expand Down
50 changes: 35 additions & 15 deletions lib/srv/uacc/uacc_linux.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build linux
// +build linux

/*
Copyright 2021 Gravitational, Inc.
Expand Down Expand Up @@ -34,6 +31,8 @@ import (
"time"
"unsafe"

"github.com/gravitational/teleport/lib/utils"

"github.com/gravitational/trace"
)

Expand All @@ -46,6 +45,20 @@ const hostMaxLen = 255
// Max username length as defined by glibc.
const userMaxLen = 32

// Sometimes the _UTMP_PATH and _WTMP_PATH macros from glibc are bad, this seems to depend on distro.
// I asked around on IRC, no one really knows why. I suspect it's another
// archaic remnant of old Unix days and that a cleanup is long overdue.
//
// In the meantime, we just try to resolve from these paths instead.

const (
utmpFilePath = "/var/run/utmp"
wtmpFilePath = "/var/log/wtmp"
// wtmpAltFilePath exists only because on some system the path is different.
// It's being used when the wtmp path is not provided and the wtmpFilePath doesn't exist.
wtmpAltFilePath = "/var/run/wtmp"
)

// Open writes a new entry to the utmp database with a tag of `USER_PROCESS`.
// This should be called when an interactive session is started.
//
Expand All @@ -70,17 +83,24 @@ func Open(utmpPath, wtmpPath string, username, hostname string, remote [4]int32,
return trace.BadParameter("tty name length exceeds OS limits")
}

// Convert Go strings into C strings that we can pass over ffi.
var cUtmpPath *C.char = nil
var cWtmpPath *C.char = nil
if len(utmpPath) > 0 {
cUtmpPath = C.CString(utmpPath)
defer C.free(unsafe.Pointer(cUtmpPath))
if utmpPath == "" {
utmpPath = utmpFilePath
}
if len(wtmpPath) > 0 {
cWtmpPath = C.CString(wtmpPath)
defer C.free(unsafe.Pointer(cWtmpPath))
// Convert Go strings into C strings that we can pass over ffi.
cUtmpPath := C.CString(utmpPath)
defer C.free(unsafe.Pointer(cUtmpPath))

if wtmpPath == "" {
// Check where wtmp is located.
if utils.FileExists(wtmpFilePath) {
wtmpPath = wtmpFilePath
} else {
wtmpPath = wtmpAltFilePath
}
}
cWtmpPath := C.CString(wtmpPath)
defer C.free(unsafe.Pointer(cWtmpPath))

cUsername := C.CString(username)
defer C.free(unsafe.Pointer(cUsername))
cHostname := C.CString(hostname)
Expand Down Expand Up @@ -136,8 +156,8 @@ func Close(utmpPath, wtmpPath string, tty *os.File) error {
}

// Convert Go strings into C strings that we can pass over ffi.
var cUtmpPath *C.char = nil
var cWtmpPath *C.char = nil
var cUtmpPath *C.char
var cWtmpPath *C.char
if len(utmpPath) > 0 {
cUtmpPath = C.CString(utmpPath)
defer C.free(unsafe.Pointer(cUtmpPath))
Expand Down Expand Up @@ -182,7 +202,7 @@ func UserWithPtyInDatabase(utmpPath string, username string) error {
}

// Convert Go strings into C strings that we can pass over ffi.
var cUtmpPath *C.char = nil
var cUtmpPath *C.char
if len(utmpPath) > 0 {
cUtmpPath = C.CString(utmpPath)
defer C.free(unsafe.Pointer(cUtmpPath))
Expand Down

0 comments on commit e954110

Please sign in to comment.