auth-token.c revision d39da67847a6c730195a65d7fb133cbfd781ac73
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Auth process maintains a random secret. Once a user authenticates the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen response to the REQUEST command from a master service is augmented with an
8363f50d7b5d605912e55c34f7f28e9f4ce01341Timo Sirainen auth_token value. This token is the SHA1 hash of the secret, the service
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen name and the username of the user that just logged in. Using this token the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen service (e.g. imap) can login to another service (e.g. imap-urlauth) to
e8bdf1be00aec45d0c6dd72ad9c8be02a3dfc778Timo Sirainen gain access to resources that require additional privileges (e.g. another
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user's e-mail).
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen*/
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
de4e3a2e1e8f82b2d3226c090b71b518b43bf9cfTimo Sirainen#include "auth-common.h"
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen#include "hex-binary.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hmac.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "sha1.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "randgen.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "read-full.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "write-full.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "safe-memset.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "auth-settings.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include "auth-token.h"
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen#include <stdlib.h>
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include <stdio.h>
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include <fcntl.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <unistd.h>
8363f50d7b5d605912e55c34f7f28e9f4ce01341Timo Sirainen#include <sys/types.h>
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen#include <sys/stat.h>
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define AUTH_TOKEN_SECRET_LEN 32
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define AUTH_TOKEN_SECRET_FNAME "auth-token-secret.dat"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic unsigned char auth_token_secret[AUTH_TOKEN_SECRET_LEN];
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainenstatic int
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainenauth_token_read_secret(const char *path,
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen unsigned char secret_r[AUTH_TOKEN_SECRET_LEN])
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen{
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen struct stat st, lst;
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen int fd, ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd = open(path, O_RDONLY);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (fd == -1) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (errno != ENOENT)
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen i_error("open(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (fstat(fd, &st) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("fstat(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_close_fd(&fd);
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen return -1;
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen }
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen /* check secret len and file type */
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen if (st.st_size != AUTH_TOKEN_SECRET_LEN || !S_ISREG(st.st_mode)) {
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen i_error("Corrupted token secret file: %s", path);
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen i_close_fd(&fd);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (unlink(path) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("unlink(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen /* verify that we're not dealing with a symbolic link */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (lstat(path, &lst) < 0) {
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen i_error("lstat(%s) failed: %m", path);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen i_close_fd(&fd);
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* check security parameters for compromise */
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen if ((st.st_mode & 07777) != 0600 ||
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen st.st_uid != geteuid() || st.st_nlink > 1 ||
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen !S_ISREG(lst.st_mode) || st.st_ino != lst.st_ino ||
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen !CMP_DEV_T(st.st_dev, lst.st_dev)) {
fc40a9a002458e372ff4b9f6f4e15239520c0bcdTimo Sirainen i_error("Compromised token secret file: %s", path);
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen i_close_fd(&fd);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen if (unlink(path) < 0)
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen i_error("unlink(%s) failed: %m", path);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen return -1;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen }
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen /* FIXME: fail here to generate new secret if stored one is too old */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen ret = read_full(fd, secret_r, AUTH_TOKEN_SECRET_LEN);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen if (ret < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("read(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen else if (ret == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_error("Token secret file unexpectedly shrank: %s", path);
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen ret = -1;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen }
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen if (close(fd) < 0)
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen i_error("close(%s) failed: %m", path);
f6f021c133f680cf3d559187524fd9abcbaae9b9Timo Sirainen
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen if (global_auth_settings->debug)
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen i_debug("Read auth token secret from %s", path);
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen return ret;
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen}
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainenstatic int
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainenauth_token_write_secret(const char *path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const unsigned char secret[AUTH_TOKEN_SECRET_LEN])
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen const char *temp_path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mode_t old_mask;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen int fd, ret;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen temp_path = t_strconcat(path, ".tmp", NULL);
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen old_mask = umask(0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen umask(old_mask);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen if (fd == -1) {
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen i_error("open(%s) failed: %m", temp_path);
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen return -1;
bc6ef0b01b99c43ee46aa796420516e89a744c30Timo Sirainen }
a0aedab7cd06125e4d73638b1bd0c01c7caa2626Timo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen ret = write_full(fd, secret, AUTH_TOKEN_SECRET_LEN);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (ret < 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_error("write(%s) failed: %m", temp_path);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (close(fd) < 0) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_error("close(%s) failed: %m", temp_path);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen ret = -1;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen }
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen if (ret < 0) {
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen if (unlink(temp_path) < 0)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_error("unlink(%s) failed: %m", temp_path);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen return -1;
3eb63515855f386449c22233d1f1baf1ddfe8a2dTimo Sirainen }
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen if (rename(temp_path, path) < 0) {
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen i_error("rename(%s, %s) failed: %m", temp_path, path);
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen if (unlink(temp_path) < 0)
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen i_error("unlink(%s) failed: %m", temp_path);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen return -1;
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen }
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen if (global_auth_settings->debug)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_debug("Wrote new auth token secret to %s", path);
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen return 0;
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen}
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainenvoid auth_token_init(void)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *secret_path =
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen t_strconcat(global_auth_settings->base_dir, "/",
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen AUTH_TOKEN_SECRET_FNAME, NULL);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (auth_token_read_secret(secret_path, auth_token_secret) < 0) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen random_fill(auth_token_secret, sizeof(auth_token_secret));
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (auth_token_write_secret(secret_path, auth_token_secret) < 0) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_error("Failed to write auth token secret file; "
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen "returned tokens will be invalid once auth restarts");
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen }
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen}
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenvoid auth_token_deinit(void)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* not very useful, but we do it anyway */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen safe_memset(auth_token_secret, 0, sizeof(auth_token_secret));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenconst char *auth_token_get(const char *service, const char *session_pid,
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen const char *username, const char *session_id)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen{
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen struct hmac_context ctx;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen unsigned char result[SHA1_RESULTLEN];
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen hmac_init(&ctx, (const unsigned char*)username, strlen(username),
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen &hash_method_sha1);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen hmac_update(&ctx, session_pid, strlen(session_pid));
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (session_id != NULL && *session_id != '\0')
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen hmac_update(&ctx, session_id, strlen(session_id));
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen hmac_update(&ctx, service, strlen(service));
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen hmac_update(&ctx, auth_token_secret, sizeof(auth_token_secret));
51078c3413b7ed4811bc725acbb1289723361ba9Timo Sirainen hmac_final(&ctx, result);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen return binary_to_hex(result, sizeof(result));
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen}
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen