/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2023 Brett Sheffield <bacs@librecast.net> */

#include "globals.h"
#include "log.h"
#include "sec.h"
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int sec_drop_privs(void)
{
	gid_t newgid = getgid();
	setgroups(1, &newgid);
	return setuid(getuid());
}

int sec_derive_symm_key(char *password)
{
	if (!password) return (errno = EINVAL), -1;
	size_t len = strlen(password) - 1;
	unsigned char salt[crypto_pwhash_SALTBYTES];
	password[len] = '\0'; /* chop newline */
	if (!len) return (errno = ECANCELED), -1;
	/* hash password to use as salt */
	crypto_generichash(salt, sizeof salt, (unsigned char *)password, len, NULL, 0);
	sodium_mlock(secretkey, sizeof secretkey);
	if (crypto_pwhash(secretkey, sizeof secretkey, password, len, salt,
			crypto_pwhash_OPSLIMIT_INTERACTIVE,
			crypto_pwhash_MEMLIMIT_INTERACTIVE,
			crypto_pwhash_ALG_DEFAULT) != 0)
		return (errno = ENOMEM), -1;
	return 0;
}

char *sec_getpassword(void)
{
	static struct termios old, new;
	password = malloc(PASSWORD_MAX);
	if (!password) return NULL;
	/* disable terminal echo */
	tcgetattr(STDIN_FILENO, &old);
	new = old;
	new.c_lflag &= ~(ECHO);
	tcsetattr(STDIN_FILENO, TCSANOW, &new);
	/* prompt for password */
	printf("Enter password (will be used to derive a symmetric key): ");
	sodium_mlock(password, PASSWORD_MAX);
	if (!fgets(password, PASSWORD_MAX, stdin))
		return (errno = ECANCELED), NULL;
	/* restore terminal settings */
	tcsetattr(STDIN_FILENO, TCSANOW, &old);
	putchar('\n');
	return password;
}

int sec_read_symm_keyfile(const char *pathname)
{
	ssize_t rc;
	int fd;
	fd = open(pathname, O_RDONLY);
	if (fd == -1) {
		ERROR("Unable to open keyfile '%s'\n", pathname);
		return -1;
	}
	sodium_mlock(secretkey, sizeof secretkey);
	rc = read(fd, secretkey, sizeof secretkey);
	if (rc == -1) perror("Error reading keyfile");
	close(fd);
	if (rc != -1 && rc != sizeof secretkey) {
		ERROR("Invalid keyfile (too short). Key must contain %zu bytes\n", sizeof secretkey);
		return (errno = EKEYREJECTED), -1;
	}
	return 0;
}

int sec_get_symm_key(void)
{
	if (keyfile) return sec_read_symm_keyfile(keyfile);
	password = getenv("LCSYNC_PASSWORD");
	if (!password) password = sec_getpassword();
	return sec_derive_symm_key(password);
}
