1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-06-03 04:19:45 +00:00
seafile-server/server/passwd-mgr.c
feiniks 7ff3282975
Support encrypted repo of V4 (#411)
* Support encrypted repo of V4

* Use aes128 for V3 encrypted repo
2020-09-22 17:06:15 +08:00

289 lines
8.2 KiB
C

#include "common.h"
#include "log.h"
#include <glib.h>
#include <timer.h>
#include "seafile-session.h"
#include "seafile-object.h"
#include "seafile-error.h"
#include "seafile-crypt.h"
#include "utils.h"
#define REAP_INTERVAL 60
#define REAP_THRESHOLD 3600
typedef struct {
int enc_version;
unsigned char key[32];
unsigned char iv[16];
guint64 expire_time;
} DecryptKey;
struct _SeafPasswdManagerPriv {
GHashTable *decrypt_keys;
CcnetTimer *reap_timer;
};
static int reap_expired_passwd (void *vmgr);
static void
decrypt_key_free (DecryptKey *key)
{
if (!key) return;
/* clear sensitive information */
memset (key->key, 0, sizeof(key->key));
memset (key->iv, 0, sizeof(key->iv));
g_free (key);
}
SeafPasswdManager *
seaf_passwd_manager_new (struct _SeafileSession *session)
{
SeafPasswdManager *mgr = g_new0 (SeafPasswdManager, 1);
mgr->session = session;
mgr->priv = g_new0 (struct _SeafPasswdManagerPriv, 1);
mgr->priv->decrypt_keys = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
(GDestroyNotify)decrypt_key_free);
return mgr;
}
int
seaf_passwd_manager_start (SeafPasswdManager *mgr)
{
mgr->priv->reap_timer = ccnet_timer_new (reap_expired_passwd,
mgr, REAP_INTERVAL * 1000);
return 1;
}
int
seaf_passwd_manager_check_passwd (SeafPasswdManager *mgr,
const char *repo_id,
const char *magic,
GError **error)
{
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
if (!repo) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Invalid repo");
return -1;
}
if (!repo->encrypted) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Repo is not encrypted");
return -1;
}
if (strcmp (magic, repo->magic) != 0) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
}
seaf_repo_unref (repo);
return 0;
}
int
seaf_passwd_manager_set_passwd (SeafPasswdManager *mgr,
const char *repo_id,
const char *user,
const char *passwd,
GError **error)
{
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
DecryptKey *crypt_key;
GString *hash_key;
if (!repo) {
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Invalid repo");
return -1;
}
if (!repo->encrypted) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Repo is not encrypted");
return -1;
}
if (repo->enc_version != 1 && repo->enc_version != 2 && repo->enc_version != 3 && repo->enc_version != 4) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
"Unsupported encryption version");
return -1;
}
if (seafile_verify_repo_passwd (repo->id, passwd,
repo->magic, repo->enc_version, repo->salt) < 0) {
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
}
crypt_key = g_new0 (DecryptKey, 1);
if (!crypt_key) {
seaf_warning ("Failed to alloc crypt key struct.\n");
seaf_repo_unref (repo);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL,
"Internal server error");
return -1;
}
if (seafile_decrypt_repo_enc_key (repo->enc_version, passwd, repo->random_key, repo->salt,
crypt_key->key, crypt_key->iv) < 0) {
seaf_repo_unref (repo);
g_free (crypt_key);
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
"Incorrect password");
return -1;
}
crypt_key->expire_time = (guint64)time(NULL) + REAP_THRESHOLD;
crypt_key->enc_version = repo->enc_version;
hash_key = g_string_new (NULL);
g_string_printf (hash_key, "%s.%s", repo_id, user);
/* g_debug ("[passwd mgr] Set passwd for %s\n", hash_key->str); */
g_hash_table_insert (mgr->priv->decrypt_keys,
g_string_free (hash_key, FALSE),
crypt_key);
seaf_repo_unref (repo);
return 0;
}
int
seaf_passwd_manager_unset_passwd (SeafPasswdManager *mgr,
const char *repo_id,
const char *user,
GError **error)
{
GString *hash_key;
hash_key = g_string_new (NULL);
g_string_printf (hash_key, "%s.%s", repo_id, user);
g_hash_table_remove (mgr->priv->decrypt_keys, hash_key->str);
g_string_free (hash_key, TRUE);
return 0;
}
gboolean
seaf_passwd_manager_is_passwd_set (SeafPasswdManager *mgr,
const char *repo_id,
const char *user)
{
GString *key = g_string_new (NULL);
gboolean ret = FALSE;
g_string_printf (key, "%s.%s", repo_id, user);
/* g_debug ("[passwd mgr] check passwd for %s\n", key->str); */
if (g_hash_table_lookup (mgr->priv->decrypt_keys, key->str) != NULL)
ret = TRUE;
g_string_free (key, TRUE);
return ret;
}
SeafileCryptKey *
seaf_passwd_manager_get_decrypt_key (SeafPasswdManager *mgr,
const char *repo_id,
const char *user)
{
GString *hash_key;
DecryptKey *crypt_key;
SeafileCryptKey *ret;
char key_hex[65], iv_hex[65];
hash_key = g_string_new (NULL);
g_string_printf (hash_key, "%s.%s", repo_id, user);
/* g_debug ("[passwd mgr] get passwd for %s.\n", hash_key->str); */
crypt_key = g_hash_table_lookup (mgr->priv->decrypt_keys, hash_key->str);
if (!crypt_key) {
g_string_free (hash_key, TRUE);
return NULL;
}
if (crypt_key->enc_version >= 2) {
rawdata_to_hex (crypt_key->key, key_hex, 32);
rawdata_to_hex (crypt_key->iv, iv_hex, 16);
} else if (crypt_key->enc_version == 1) {
rawdata_to_hex (crypt_key->key, key_hex, 16);
rawdata_to_hex (crypt_key->iv, iv_hex, 16);
}
ret = seafile_crypt_key_new ();
g_object_set (ret, "key", key_hex, "iv", iv_hex, NULL);
g_string_free (hash_key, TRUE);
return ret;
}
int
seaf_passwd_manager_get_decrypt_key_raw (SeafPasswdManager *mgr,
const char *repo_id,
const char *user,
unsigned char *key_out,
unsigned char *iv_out)
{
GString *hash_key;
DecryptKey *crypt_key;
hash_key = g_string_new (NULL);
g_string_printf (hash_key, "%s.%s", repo_id, user);
crypt_key = g_hash_table_lookup (mgr->priv->decrypt_keys, hash_key->str);
if (!crypt_key) {
g_string_free (hash_key, TRUE);
return -1;
}
g_string_free (hash_key, TRUE);
if (crypt_key->enc_version == 1) {
memcpy (key_out, crypt_key->key, 16);
memcpy (iv_out, crypt_key->iv, 16);
} else if (crypt_key->enc_version >= 2) {
memcpy (key_out, crypt_key->key, 32);
memcpy (iv_out, crypt_key->iv, 16);
}
return 0;
}
static int
reap_expired_passwd (void *vmgr)
{
SeafPasswdManager *mgr = vmgr;
GHashTableIter iter;
gpointer key, value;
DecryptKey *crypt_key;
guint64 now = (guint64)time(NULL);
g_hash_table_iter_init (&iter, mgr->priv->decrypt_keys);
while (g_hash_table_iter_next (&iter, &key, &value)) {
crypt_key = value;
if (crypt_key->expire_time <= now) {
/* g_debug ("[passwd mgr] Remove passwd for %s\n", (char *)key); */
g_hash_table_iter_remove (&iter);
}
}
return 1;
}