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