1 #include "common.h"
2 #include "log.h"
3
4 #include <glib.h>
5 #include <timer.h>
6
7 #include "seafile-session.h"
8 #include "seafile-object.h"
9 #include "seafile-error.h"
10 #include "seafile-crypt.h"
11
12 #include "utils.h"
13
14 #define REAP_INTERVAL 60
15 #define REAP_THRESHOLD 3600
16
17 typedef struct {
18 int enc_version;
19 unsigned char key[32];
20 unsigned char iv[16];
21 guint64 expire_time;
22 } DecryptKey;
23
24 struct _SeafPasswdManagerPriv {
25 GHashTable *decrypt_keys;
26 CcnetTimer *reap_timer;
27 };
28
29 static int reap_expired_passwd (void *vmgr);
30
31 static void
decrypt_key_free(DecryptKey * key)32 decrypt_key_free (DecryptKey *key)
33 {
34 if (!key) return;
35
36 /* clear sensitive information */
37 memset (key->key, 0, sizeof(key->key));
38 memset (key->iv, 0, sizeof(key->iv));
39 g_free (key);
40 }
41
42 SeafPasswdManager *
seaf_passwd_manager_new(struct _SeafileSession * session)43 seaf_passwd_manager_new (struct _SeafileSession *session)
44 {
45 SeafPasswdManager *mgr = g_new0 (SeafPasswdManager, 1);
46
47 mgr->session = session;
48 mgr->priv = g_new0 (struct _SeafPasswdManagerPriv, 1);
49 mgr->priv->decrypt_keys = g_hash_table_new_full (g_str_hash, g_str_equal,
50 g_free,
51 (GDestroyNotify)decrypt_key_free);
52
53 return mgr;
54 }
55
56 int
seaf_passwd_manager_start(SeafPasswdManager * mgr)57 seaf_passwd_manager_start (SeafPasswdManager *mgr)
58 {
59 mgr->priv->reap_timer = ccnet_timer_new (reap_expired_passwd,
60 mgr, REAP_INTERVAL * 1000);
61 return 1;
62 }
63
64 int
seaf_passwd_manager_check_passwd(SeafPasswdManager * mgr,const char * repo_id,const char * magic,GError ** error)65 seaf_passwd_manager_check_passwd (SeafPasswdManager *mgr,
66 const char *repo_id,
67 const char *magic,
68 GError **error)
69 {
70 SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
71
72 if (!repo) {
73 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
74 "Invalid repo");
75 return -1;
76 }
77
78 if (!repo->encrypted) {
79 seaf_repo_unref (repo);
80 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
81 "Repo is not encrypted");
82 return -1;
83 }
84
85 if (strcmp (magic, repo->magic) != 0) {
86 seaf_repo_unref (repo);
87 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
88 "Incorrect password");
89 return -1;
90 }
91
92 seaf_repo_unref (repo);
93
94 return 0;
95 }
96
97 int
seaf_passwd_manager_set_passwd(SeafPasswdManager * mgr,const char * repo_id,const char * user,const char * passwd,GError ** error)98 seaf_passwd_manager_set_passwd (SeafPasswdManager *mgr,
99 const char *repo_id,
100 const char *user,
101 const char *passwd,
102 GError **error)
103 {
104 SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
105 DecryptKey *crypt_key;
106 GString *hash_key;
107
108 if (!repo) {
109 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
110 "Invalid repo");
111 return -1;
112 }
113
114 if (!repo->encrypted) {
115 seaf_repo_unref (repo);
116 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
117 "Repo is not encrypted");
118 return -1;
119 }
120
121 if (repo->enc_version != 1 && repo->enc_version != 2 && repo->enc_version != 3 && repo->enc_version != 4) {
122 seaf_repo_unref (repo);
123 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_BAD_ARGS,
124 "Unsupported encryption version");
125 return -1;
126 }
127
128 if (seafile_verify_repo_passwd (repo->id, passwd,
129 repo->magic, repo->enc_version, repo->salt) < 0) {
130 seaf_repo_unref (repo);
131 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
132 "Incorrect password");
133 return -1;
134 }
135
136 crypt_key = g_new0 (DecryptKey, 1);
137 if (!crypt_key) {
138 seaf_warning ("Failed to alloc crypt key struct.\n");
139 seaf_repo_unref (repo);
140 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_INTERNAL,
141 "Internal server error");
142 return -1;
143 }
144
145 if (seafile_decrypt_repo_enc_key (repo->enc_version, passwd, repo->random_key, repo->salt,
146 crypt_key->key, crypt_key->iv) < 0) {
147 seaf_repo_unref (repo);
148 g_free (crypt_key);
149 g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
150 "Incorrect password");
151 return -1;
152 }
153 crypt_key->expire_time = (guint64)time(NULL) + REAP_THRESHOLD;
154 crypt_key->enc_version = repo->enc_version;
155
156 hash_key = g_string_new (NULL);
157 g_string_printf (hash_key, "%s.%s", repo_id, user);
158
159 /* g_debug ("[passwd mgr] Set passwd for %s\n", hash_key->str); */
160
161 g_hash_table_insert (mgr->priv->decrypt_keys,
162 g_string_free (hash_key, FALSE),
163 crypt_key);
164 seaf_repo_unref (repo);
165
166 return 0;
167 }
168
169 int
seaf_passwd_manager_unset_passwd(SeafPasswdManager * mgr,const char * repo_id,const char * user,GError ** error)170 seaf_passwd_manager_unset_passwd (SeafPasswdManager *mgr,
171 const char *repo_id,
172 const char *user,
173 GError **error)
174 {
175 GString *hash_key;
176
177 hash_key = g_string_new (NULL);
178 g_string_printf (hash_key, "%s.%s", repo_id, user);
179 g_hash_table_remove (mgr->priv->decrypt_keys, hash_key->str);
180 g_string_free (hash_key, TRUE);
181
182 return 0;
183 }
184
185 gboolean
seaf_passwd_manager_is_passwd_set(SeafPasswdManager * mgr,const char * repo_id,const char * user)186 seaf_passwd_manager_is_passwd_set (SeafPasswdManager *mgr,
187 const char *repo_id,
188 const char *user)
189 {
190 GString *key = g_string_new (NULL);
191 gboolean ret = FALSE;
192
193 g_string_printf (key, "%s.%s", repo_id, user);
194 /* g_debug ("[passwd mgr] check passwd for %s\n", key->str); */
195 if (g_hash_table_lookup (mgr->priv->decrypt_keys, key->str) != NULL)
196 ret = TRUE;
197 g_string_free (key, TRUE);
198
199 return ret;
200 }
201
202 SeafileCryptKey *
seaf_passwd_manager_get_decrypt_key(SeafPasswdManager * mgr,const char * repo_id,const char * user)203 seaf_passwd_manager_get_decrypt_key (SeafPasswdManager *mgr,
204 const char *repo_id,
205 const char *user)
206 {
207 GString *hash_key;
208 DecryptKey *crypt_key;
209 SeafileCryptKey *ret;
210 char key_hex[65], iv_hex[65];
211
212 hash_key = g_string_new (NULL);
213 g_string_printf (hash_key, "%s.%s", repo_id, user);
214
215 /* g_debug ("[passwd mgr] get passwd for %s.\n", hash_key->str); */
216
217 crypt_key = g_hash_table_lookup (mgr->priv->decrypt_keys, hash_key->str);
218 if (!crypt_key) {
219 g_string_free (hash_key, TRUE);
220 return NULL;
221 }
222
223 if (crypt_key->enc_version >= 2) {
224 rawdata_to_hex (crypt_key->key, key_hex, 32);
225 rawdata_to_hex (crypt_key->iv, iv_hex, 16);
226 } else if (crypt_key->enc_version == 1) {
227 rawdata_to_hex (crypt_key->key, key_hex, 16);
228 rawdata_to_hex (crypt_key->iv, iv_hex, 16);
229 }
230
231 ret = seafile_crypt_key_new ();
232 g_object_set (ret, "key", key_hex, "iv", iv_hex, NULL);
233
234 g_string_free (hash_key, TRUE);
235 return ret;
236 }
237
238 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)239 seaf_passwd_manager_get_decrypt_key_raw (SeafPasswdManager *mgr,
240 const char *repo_id,
241 const char *user,
242 unsigned char *key_out,
243 unsigned char *iv_out)
244 {
245 GString *hash_key;
246 DecryptKey *crypt_key;
247
248 hash_key = g_string_new (NULL);
249 g_string_printf (hash_key, "%s.%s", repo_id, user);
250
251 crypt_key = g_hash_table_lookup (mgr->priv->decrypt_keys, hash_key->str);
252 if (!crypt_key) {
253 g_string_free (hash_key, TRUE);
254 return -1;
255 }
256 g_string_free (hash_key, TRUE);
257
258 if (crypt_key->enc_version == 1) {
259 memcpy (key_out, crypt_key->key, 16);
260 memcpy (iv_out, crypt_key->iv, 16);
261 } else if (crypt_key->enc_version >= 2) {
262 memcpy (key_out, crypt_key->key, 32);
263 memcpy (iv_out, crypt_key->iv, 16);
264 }
265
266 return 0;
267 }
268
269 static int
reap_expired_passwd(void * vmgr)270 reap_expired_passwd (void *vmgr)
271 {
272 SeafPasswdManager *mgr = vmgr;
273 GHashTableIter iter;
274 gpointer key, value;
275 DecryptKey *crypt_key;
276 guint64 now = (guint64)time(NULL);
277
278 g_hash_table_iter_init (&iter, mgr->priv->decrypt_keys);
279 while (g_hash_table_iter_next (&iter, &key, &value)) {
280 crypt_key = value;
281 if (crypt_key->expire_time <= now) {
282 /* g_debug ("[passwd mgr] Remove passwd for %s\n", (char *)key); */
283 g_hash_table_iter_remove (&iter);
284 }
285 }
286
287 return 1;
288 }
289