bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch/* Auth process maintains a random secret. Once a user authenticates the
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch response to the REQUEST command from a master service is augmented with an
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch auth_token value. This token is the SHA1 hash of the secret, the service
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch name and the username of the user that just logged in. Using this token the
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch service (e.g. imap) can login to another service (e.g. imap-urlauth) to
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch gain access to resources that require additional privileges (e.g. another
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch user's e-mail).
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch*/
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "auth-common.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "hex-binary.h"
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz#include "hmac.h"
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz#include "sha1.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "randgen.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "read-full.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "write-full.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "safe-memset.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "auth-settings.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include "auth-token.h"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include <stdio.h>
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include <fcntl.h>
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include <unistd.h>
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include <sys/types.h>
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#include <sys/stat.h>
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#define AUTH_TOKEN_SECRET_LEN 32
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch#define AUTH_TOKEN_SECRET_FNAME "auth-token-secret.dat"
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschstatic unsigned char auth_token_secret[AUTH_TOKEN_SECRET_LEN];
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschstatic int
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschauth_token_read_secret(const char *path,
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch unsigned char secret_r[AUTH_TOKEN_SECRET_LEN])
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch{
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch struct stat st, lst;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch int fd, ret;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch fd = open(path, O_RDONLY);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (fd == -1) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (errno != ENOENT)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("open(%s) failed: %m", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (fstat(fd, &st) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("fstat(%s) failed: %m", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_close_fd(&fd);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* check secret len and file type */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("Corrupted token secret file: %s", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_close_fd(&fd);
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen i_unlink(path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* verify that we're not dealing with a symbolic link */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (lstat(path, &lst) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("lstat(%s) failed: %m", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_close_fd(&fd);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* check security parameters for compromise */
d39da67847a6c730195a65d7fb133cbfd781ac73Timo Sirainen if ((st.st_mode & 07777) != 0600 ||
d39da67847a6c730195a65d7fb133cbfd781ac73Timo Sirainen st.st_uid != geteuid() || st.st_nlink > 1 ||
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino ||
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch !CMP_DEV_T(st.st_dev, lst.st_dev)) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("Compromised token secret file: %s", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_close_fd(&fd);
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen i_unlink(path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* FIXME: fail here to generate new secret if stored one is too old */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (ret < 0)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("read(%s) failed: %m", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch else if (ret == 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("Token secret file unexpectedly shrank: %s", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch ret = -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (close(fd) < 0)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("close(%s) failed: %m", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (global_auth_settings->debug)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_debug("Read auth token secret from %s", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return ret;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch}
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschstatic int
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschauth_token_write_secret(const char *path,
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const unsigned char secret[AUTH_TOKEN_SECRET_LEN])
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch{
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const char *temp_path;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch mode_t old_mask;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch int fd, ret;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch temp_path = t_strconcat(path, ".tmp", NULL);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch old_mask = umask(0);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch umask(old_mask);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (fd == -1) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("open(%s) failed: %m", temp_path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch ret = write_full(fd, secret, AUTH_TOKEN_SECRET_LEN);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (ret < 0)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("write(%s) failed: %m", temp_path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (close(fd) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("close(%s) failed: %m", temp_path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch ret = -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (ret < 0) {
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen i_unlink(temp_path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (rename(temp_path, path) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("rename(%s, %s) failed: %m", temp_path, path);
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen i_unlink(temp_path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return -1;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (global_auth_settings->debug)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_debug("Wrote new auth token secret to %s", path);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return 0;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch}
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschvoid auth_token_init(void)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch{
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const char *secret_path =
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch t_strconcat(global_auth_settings->base_dir, "/",
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch AUTH_TOKEN_SECRET_FNAME, NULL);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (auth_token_read_secret(secret_path, auth_token_secret) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch random_fill(auth_token_secret, sizeof(auth_token_secret));
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (auth_token_write_secret(secret_path, auth_token_secret) < 0) {
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch i_error("Failed to write auth token secret file; "
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch "returned tokens will be invalid once auth restarts");
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch }
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch}
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschvoid auth_token_deinit(void)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch{
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch /* not very useful, but we do it anyway */
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch safe_memset(auth_token_secret, 0, sizeof(auth_token_secret));
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch}
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Boschconst char *auth_token_get(const char *service, const char *session_pid,
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch const char *username, const char *session_id)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch{
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz struct hmac_context ctx;
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch unsigned char result[SHA1_RESULTLEN];
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_init(&ctx, (const unsigned char*)username, strlen(username),
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz &hash_method_sha1);
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_update(&ctx, session_pid, strlen(session_pid));
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (session_id != NULL && *session_id != '\0')
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_update(&ctx, session_id, strlen(session_id));
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_update(&ctx, service, strlen(service));
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
0a3769a4ef3afbbbd05df38f43ec7382fd65a2b6Florian Zeitz hmac_final(&ctx, result);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch return binary_to_hex(result, sizeof(result));
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch}