61 #define ENV_HOME getenv("USERPROFILE") |
61 #define ENV_HOME getenv("USERPROFILE") |
62 #else |
62 #else |
63 #define ENV_HOME getenv("HOME") |
63 #define ENV_HOME getenv("HOME") |
64 #endif /* _WIN32 */ |
64 #endif /* _WIN32 */ |
65 |
65 |
66 static CxMap* repos; |
66 static CxMap *repos; |
67 static CxMap* keys; |
67 static CxMap *keys; |
68 |
68 |
69 static DavConfig* davconfig; |
69 static DavConfig *davconfig; |
70 static PwdStore* pstore; |
70 static PwdStore *pstore; |
71 |
71 |
72 static char* secretstore_unlock_cmd; |
72 static char *secretstore_unlock_cmd; |
73 static char* secretstore_lock_cmd; |
73 static char *secretstore_lock_cmd; |
74 |
74 |
75 int check_config_dir(void) { |
75 int check_config_dir(void) { |
76 char* file = util_concat_path(ENV_HOME, ".dav"); |
76 char *file = util_concat_path(ENV_HOME, ".dav"); |
77 int ret = 0; |
77 int ret = 0; |
78 if (util_mkdir(file, S_IRWXU)) { |
78 if(util_mkdir(file, S_IRWXU)) { |
79 if (errno != EEXIST) { |
79 if(errno != EEXIST) { |
80 ret = 1; |
80 ret = 1; |
81 } |
81 } |
82 } |
82 } |
83 free(file); |
83 free(file); |
84 return ret; |
84 return ret; |
85 } |
85 } |
86 |
86 |
87 static DavContext* context; |
87 static DavContext *context; |
88 |
88 |
89 void create_default_config(char* file) { |
89 void create_default_config(char *file) { |
90 xmlDoc* doc = xmlNewDoc(BAD_CAST "1.0"); |
90 xmlDoc *doc = xmlNewDoc(BAD_CAST "1.0"); |
91 xmlNode* root = xmlNewNode(NULL, BAD_CAST "configuration"); |
91 xmlNode *root = xmlNewNode(NULL, BAD_CAST "configuration"); |
92 xmlDocSetRootElement(doc, root); |
92 xmlDocSetRootElement(doc, root); |
93 xmlSaveFormatFileEnc(file, doc, "UTF-8", 1); |
93 xmlSaveFormatFileEnc(file, doc, "UTF-8", 1); |
94 xmlFreeDoc(doc); |
94 xmlFreeDoc(doc); |
95 } |
95 } |
96 |
96 |
97 char* config_file_path(char* name) { |
97 char* config_file_path(char *name) { |
98 char* davd = util_concat_path(ENV_HOME, ".dav"); |
98 char *davd = util_concat_path(ENV_HOME, ".dav"); |
99 if (!davd) { |
99 if(!davd) { |
100 return NULL; |
100 return NULL; |
101 } |
101 } |
102 char* path = util_concat_path(davd, name); |
102 char *path = util_concat_path(davd, name); |
103 free(davd); |
103 free(davd); |
104 return path; |
104 return path; |
105 } |
105 } |
106 |
106 |
107 cxmutstr config_load_file(const char* path) { |
107 cxmutstr config_load_file(const char *path) { |
108 FILE* file = sys_fopen(path, "r"); |
108 FILE *file = sys_fopen(path, "r"); |
109 if (!file) { |
109 if(!file) { |
110 return (cxmutstr) { NULL, 0 }; |
110 return (cxmutstr){NULL,0}; |
111 } |
111 } |
112 |
112 |
113 CxBuffer buf; |
113 CxBuffer buf; |
114 cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); |
114 cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); |
115 cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); |
115 cx_stream_copy(file, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); |
116 fclose(file); |
116 fclose(file); |
117 |
117 |
118 return cx_mutstrn(buf.space, buf.size); |
118 return cx_mutstrn(buf.space, buf.size); |
119 } |
119 } |
120 |
120 |
121 int load_config(DavContext *ctx) { |
121 int load_config(DavContext *ctx) { |
122 context = ctx; |
122 context = ctx; |
165 DavConfig* get_config(void) { |
165 DavConfig* get_config(void) { |
166 return davconfig; |
166 return davconfig; |
167 } |
167 } |
168 |
168 |
169 int store_config(void) { |
169 int store_config(void) { |
170 if (check_config_dir()) { |
170 if(check_config_dir()) { |
171 return 1; |
171 return 1; |
172 } |
172 } |
173 |
173 |
174 CxBuffer* buf = dav_config2buf(davconfig); |
174 CxBuffer *buf = dav_config2buf(davconfig); |
175 if (!buf) { |
175 if(!buf) { |
176 return 1; |
176 return 1; |
177 } |
177 } |
178 |
178 |
179 char* file = util_concat_path(ENV_HOME, ".dav/config.xml"); |
179 char *file = util_concat_path(ENV_HOME, ".dav/config.xml"); |
180 FILE* cout = sys_fopen(file, "w"); |
180 FILE *cout = sys_fopen(file, "w"); |
181 if (!cout) { |
181 if(!cout) { |
182 cxBufferFree(buf); |
182 cxBufferFree(buf); |
183 return 1; |
183 return 1; |
184 } |
184 } |
185 |
185 |
186 // should only fail if we run out of disk space or something like that |
186 // should only fail if we run out of disk space or something like that |
187 // in that case, the config file is only destroyed |
187 // in that case, the config file is only destroyed |
188 // could only be prevented, if we write to a temp file first and than |
188 // could only be prevented, if we write to a temp file first and than |
189 // rename it |
189 // rename it |
190 fwrite(buf->space, buf->size, 1, cout); |
190 fwrite(buf->space, buf->size, 1, cout); |
191 |
191 |
192 cxBufferFree(buf); |
192 cxBufferFree(buf); |
193 fclose(cout); |
193 fclose(cout); |
194 |
194 |
195 return 0; |
195 return 0; |
196 } |
196 } |
197 |
197 |
198 void free_config(void) { |
198 void free_config(void) { |
199 if (davconfig) { |
199 if(davconfig) { |
200 dav_config_free(davconfig); |
200 dav_config_free(davconfig); |
201 } |
201 } |
202 } |
202 } |
203 |
203 |
204 cxmutstr load_key_file(const char* filename) { |
204 cxmutstr load_key_file(const char *filename) { |
205 cxmutstr k; |
205 cxmutstr k; |
206 k.ptr = NULL; |
206 k.ptr = NULL; |
207 k.length = 0; |
207 k.length = 0; |
208 |
208 |
209 FILE* file = NULL; |
209 FILE *file = NULL; |
210 if (filename[0] == '/') { |
210 if(filename[0] == '/') { |
211 file = sys_fopen(filename, "r"); |
211 file = sys_fopen(filename, "r"); |
212 } |
212 } else { |
213 else { |
213 char *path = util_concat_path(ENV_HOME, ".dav/"); |
214 char* path = util_concat_path(ENV_HOME, ".dav/"); |
214 char *p2 = util_concat_path(path, filename); |
215 char* p2 = util_concat_path(path, filename); |
|
216 file = sys_fopen(p2, "r"); |
215 file = sys_fopen(p2, "r"); |
217 free(path); |
216 free(path); |
218 free(p2); |
217 free(p2); |
219 } |
218 } |
220 |
219 |
221 if (!file) { |
220 if(!file) { |
222 fprintf(stderr, "Error: cannot load keyfile %s\n", filename); |
221 fprintf(stderr, "Error: cannot load keyfile %s\n", filename); |
223 return k; |
222 return k; |
224 } |
223 } |
225 |
224 |
226 char* data = malloc(256); |
225 char *data = malloc(256); |
227 size_t r = fread(data, 1, 256, file); |
226 size_t r = fread(data, 1, 256, file); |
228 k.ptr = data; |
227 k.ptr = data; |
229 k.length = r; |
228 k.length = r; |
230 |
229 |
231 fclose(file); |
230 fclose(file); |
232 return k; |
231 return k; |
233 } |
232 } |
234 |
233 |
235 static char* get_attr_content(xmlNode* node) { |
|
236 // TODO: remove code duplication (util_xml_get_text) |
|
237 while (node) { |
|
238 if (node->type == XML_TEXT_NODE) { |
|
239 return (char*)node->content; |
|
240 } |
|
241 node = node->next; |
|
242 } |
|
243 return NULL; |
|
244 } |
|
245 |
|
246 int load_namespace(const xmlNode* node) { |
|
247 const char* prefix = NULL; |
|
248 const char* uri = NULL; |
|
249 |
|
250 xmlAttr* attr = node->properties; |
|
251 while (attr) { |
|
252 if (attr->type == XML_ATTRIBUTE_NODE) { |
|
253 char* value = get_attr_content(attr->children); |
|
254 if (!value) { |
|
255 print_error( |
|
256 node->line, |
|
257 "missing value for attribute %s\n", (char*)attr->name); |
|
258 return 1; |
|
259 } |
|
260 if (xstreq(attr->name, "prefix")) { |
|
261 prefix = value; |
|
262 } |
|
263 else if (xstreq(attr->name, "uri")) { |
|
264 uri = value; |
|
265 } |
|
266 else { |
|
267 print_error( |
|
268 node->line, |
|
269 "unexpected attribute %s\n", (char*)attr->name); |
|
270 return 1; |
|
271 } |
|
272 } |
|
273 attr = attr->next; |
|
274 } |
|
275 |
|
276 if (!prefix) { |
|
277 print_error(node->line, "missing prefix attribute\n"); |
|
278 return 1; |
|
279 } |
|
280 if (!uri) { |
|
281 print_error(node->line, "missing uri attribute\n"); |
|
282 return 1; |
|
283 } |
|
284 |
|
285 if (dav_get_namespace(context, prefix)) { |
|
286 print_error(node->line, "namespace prefix '%s' already used\n", prefix); |
|
287 return 1; |
|
288 } |
|
289 |
|
290 return dav_add_namespace(context, prefix, uri); |
|
291 } |
|
292 |
|
293 int load_secretstore(const xmlNode* node) { |
|
294 // currently only one secretstore is supported |
|
295 |
|
296 if (!pstore) { |
|
297 return 0; |
|
298 } |
|
299 |
|
300 node = node->children; |
|
301 int error = 0; |
|
302 while (node) { |
|
303 if (node->type == XML_ELEMENT_NODE) { |
|
304 char* value = util_xml_get_text(node); |
|
305 if (value) { |
|
306 if (xstreq(node->name, "unlock-command")) { |
|
307 pstore->unlock_cmd = strdup(value); |
|
308 } |
|
309 else if (xstreq(node->name, "lock-command")) { |
|
310 pstore->lock_cmd = strdup(value); |
|
311 } |
|
312 } |
|
313 } |
|
314 node = node->next; |
|
315 } |
|
316 |
|
317 return error; |
|
318 } |
|
319 |
|
320 PwdStore* get_pwdstore(void) { |
234 PwdStore* get_pwdstore(void) { |
321 return pstore; |
235 return pstore; |
322 } |
236 } |
323 |
237 |
324 int pwdstore_save(PwdStore* pwdstore) { |
238 int pwdstore_save(PwdStore *pwdstore) { |
325 if (check_config_dir()) { |
239 if(check_config_dir()) { |
326 return 1; |
240 return 1; |
327 } |
241 } |
328 |
242 |
329 char* pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); |
243 char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); |
330 int ret = pwdstore_store(pwdstore, pwfile); |
244 int ret = pwdstore_store(pwdstore, pwfile); |
331 free(pwfile); |
245 free(pwfile); |
332 return ret; |
246 return ret; |
333 } |
247 } |
334 |
|
335 |
|
336 |
|
337 static int decrypt_secrets(PwdStore *secrets) { |
|
338 char *ps_password = NULL; |
|
339 if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) { |
|
340 CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); |
|
341 if(!util_exec_command(secrets->unlock_cmd, cmd_out)) { |
|
342 // command successful, get first line from output without newline |
|
343 // and use that as password for the secretstore |
|
344 size_t len = 0; |
|
345 for(size_t i=0;i<=cmd_out->size;i++) { |
|
346 if(i == cmd_out->size || cmd_out->space[i] == '\n') { |
|
347 len = i; |
|
348 break; |
|
349 } |
|
350 } |
|
351 if(len > 0) { |
|
352 ps_password = malloc(len + 1); |
|
353 memcpy(ps_password, cmd_out->space, len); |
|
354 ps_password[len] = 0; |
|
355 } |
|
356 } |
|
357 cxBufferFree(cmd_out); |
|
358 } |
|
359 |
|
360 return 1; |
|
361 // TODO |
|
362 /* |
|
363 if(!ps_password) { |
|
364 ps_password = util_password_input("Master password: "); |
|
365 if(!ps_password) { |
|
366 return 1; |
|
367 } |
|
368 } |
|
369 |
|
370 if(pwdstore_setpassword(secrets, ps_password)) { |
|
371 fprintf(stderr, "Error: cannot create key from password\n"); |
|
372 return 1; |
|
373 } |
|
374 if(pwdstore_decrypt(secrets)) { |
|
375 fprintf(stderr, "Error: cannot decrypt secrets store\n"); |
|
376 return 1; |
|
377 } |
|
378 return 0; |
|
379 */ |
|
380 } |
|
381 |
|
382 |
248 |
383 typedef struct CredLocation { |
249 typedef struct CredLocation { |
384 char *id; |
250 char *id; |
385 char *location; |
251 char *location; |
386 } CredLocation; |
252 } CredLocation; |
393 // c->id is not a copy, therefore we don't have to free it |
259 // c->id is not a copy, therefore we don't have to free it |
394 free(c->location); |
260 free(c->location); |
395 free(c); |
261 free(c); |
396 } |
262 } |
397 |
263 |
398 static int get_stored_credentials(char *credid, char **user, char **password) { |
264 int get_stored_credentials(char *credid, char **user, char **password) { |
399 return 0; |
|
400 // TODO |
|
401 /* |
|
402 if(!credid) { |
265 if(!credid) { |
403 return 0; |
266 return 0; |
404 } |
267 } |
|
268 |
405 PwdStore *secrets = get_pwdstore(); |
269 PwdStore *secrets = get_pwdstore(); |
406 if(!secrets) { |
270 if(!secrets) { |
407 fprintf(stderr, "Error: no secrets store available\n"); |
271 fprintf(stderr, "Error: no secrets store available\n"); |
408 return 0; |
272 return 0; |
409 } |
273 } |
410 |
274 |
411 if(pwdstore_has_id(secrets, credid)) { |
275 if(pwdstore_has_id(secrets, credid)) { |
412 if(!secrets->isdecrypted) { |
276 if(!secrets->isdecrypted) { |
413 if(decrypt_secrets(a, secrets)) { |
277 if(pwdstore_decrypt_secrets(secrets)) { |
414 return 0; |
278 return 0; |
415 } |
279 } |
416 } |
280 } |
417 |
281 |
418 PwdEntry *s_cred = pwdstore_get(secrets, credid); |
282 PwdEntry *s_cred = pwdstore_get(secrets, credid); |
419 if(s_cred) { |
283 if(s_cred) { |
420 *user = s_cred->user; |
284 *user = s_cred->user; |
421 *password = s_cred->password; |
285 *password = s_cred->password; |
422 return 1; |
286 return 1; |
423 } |
287 } |
424 } else { |
288 } else { |
425 fprintf(stderr, "Error: credentials id '%s' not found\n", credid); |
289 fprintf(stderr, "Error: credentials id '%s' not found\n", credid); |
426 } |
290 } |
427 |
291 |
428 return 0; |
292 return 0; |
429 */ |
293 } |
430 } |
294 |
431 |
295 |
432 |
296 int get_location_credentials(DavCfgRepository *repo, const char *path, char **user, char **password) { |
433 static int get_location_credentials(DavCfgRepository *repo, const char *path, char **user, char **password) { |
|
434 PwdStore *secrets = get_pwdstore(); |
297 PwdStore *secrets = get_pwdstore(); |
435 if(!secrets) { |
298 if(!secrets) { |
436 return 0; |
299 return 0; |
437 } |
300 } |
438 |
301 |
439 /* |
302 /* |
440 * The list secrets->location contains urls or repo names as |
303 * The list secrets->location contains urls or repo names as |
441 * location strings. We need a list, that contains only urls |
304 * location strings. We need a list, that contains only urls |
442 */ |
305 */ |
443 CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS); |
306 CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS); |
444 cxDefineDestructor(locations, free_cred_location); |
307 cxDefineDestructor(locations, free_cred_location); |
445 CxIterator i = cxListIterator(secrets->locations); |
308 CxIterator i = cxListIterator(secrets->locations); |
446 cx_foreach(PwdIndexEntry*, e, i) { |
309 cx_foreach(PwdIndexEntry*, e, i) { |
447 CxIterator entry_iter = cxListIterator(e->locations); |
310 CxIterator entry_iter = cxListIterator(e->locations); |
455 free(rpath.ptr); |
318 free(rpath.ptr); |
456 } |
319 } |
457 } |
320 } |
458 // the list must be sorted |
321 // the list must be sorted |
459 cxListSort(locations); |
322 cxListSort(locations); |
460 |
323 |
461 // create full request url string and remove protocol prefix |
324 // create full request url string and remove protocol prefix |
462 cxmutstr req_url_proto = util_concat_path_s(cx_strcast(repo->url.value), cx_str(path)); |
325 cxmutstr req_url_proto = util_concat_path_s(cx_strcast(repo->url.value), cx_str(path)); |
463 cxstring req_url = cx_strcast(req_url_proto); |
326 cxstring req_url = cx_strcast(req_url_proto); |
464 if(cx_strprefix(req_url, CX_STR("http://"))) { |
327 if(cx_strprefix(req_url, CX_STR("http://"))) { |
465 req_url = cx_strsubs(req_url, 7); |
328 req_url = cx_strsubs(req_url, 7); |
466 } else if(cx_strprefix(req_url, CX_STR("https://"))) { |
329 } else if(cx_strprefix(req_url, CX_STR("https://"))) { |
467 req_url = cx_strsubs(req_url, 8); |
330 req_url = cx_strsubs(req_url, 8); |
468 } |
331 } |
469 |
332 |
470 // iterate over sorted locations and check if a location is a prefix |
333 // iterate over sorted locations and check if a location is a prefix |
471 // of the requested url |
334 // of the requested url |
472 char *id = NULL; |
335 char *id = NULL; |
473 int ret = 0; |
336 int ret = 0; |
474 i = cxListIterator(locations); |
337 i = cxListIterator(locations); |
475 cx_foreach(CredLocation*, cred, i) { |
338 cx_foreach(CredLocation*, cred, i) { |
476 cxstring cred_url = cx_str(cred->location); |
339 cxstring cred_url = cx_str(cred->location); |
477 |
340 |
478 // remove protocol prefix |
341 // remove protocol prefix |
479 if(cx_strprefix(cred_url, CX_STR("http://"))) { |
342 if(cx_strprefix(cred_url, CX_STR("http://"))) { |
480 cred_url = cx_strsubs(cred_url, 7); |
343 cred_url = cx_strsubs(cred_url, 7); |
481 } else if(cx_strprefix(cred_url, CX_STR("https://"))) { |
344 } else if(cx_strprefix(cred_url, CX_STR("https://"))) { |
482 cred_url = cx_strsubs(cred_url, 8); |
345 cred_url = cx_strsubs(cred_url, 8); |
483 } |
346 } |
484 |
347 |
485 if(cx_strprefix(req_url, cred_url)) { |
348 if(cx_strprefix(req_url, cred_url)) { |
486 id = cred->id; |
349 id = cred->id; |
487 break; |
350 break; |
488 } |
351 } |
489 } |
352 } |
490 |
353 |
491 // if an id is found and we can access the decrypted secret store |
354 // if an id is found and we can access the decrypted secret store |
492 // we can set the user/password |
355 // we can set the user/password |
493 if(id && (secrets->isdecrypted || !decrypt_secrets(secrets))) { |
356 if(id && (secrets->isdecrypted || !pwdstore_decrypt_secrets(secrets))) { |
494 PwdEntry *cred = pwdstore_get(secrets, id); |
357 PwdEntry *cred = pwdstore_get(secrets, id); |
495 if(cred) { |
358 if(cred) { |
496 *user = cred->user; |
359 *user = cred->user; |
497 *password = cred->password; |
360 *password = cred->password; |
498 ret = 1; |
361 ret = 1; |
499 } |
362 } |
500 } |
363 } |
501 |
364 |
502 free(req_url_proto.ptr); |
365 free(req_url_proto.ptr); |
503 cxListDestroy(locations); |
366 cxListDestroy(locations); |
504 |
367 |
505 return ret; |
368 return ret; |
506 } |
369 } |
507 |
|
508 |
|
509 DavSession* connect_to_repo(DavContext *ctx, DavCfgRepository *repo, const char *path, dav_auth_func authfunc) { |
|
510 cxmutstr decodedpw = dav_repository_get_decodedpassword(repo); |
|
511 |
|
512 char *user = repo->user.value.ptr; |
|
513 char *password = decodedpw.ptr; |
|
514 |
|
515 if(!user && !password) { |
|
516 if(!get_stored_credentials(repo->stored_user.value.ptr, &user, &password)) { |
|
517 get_location_credentials(repo, path, &user, &password); |
|
518 } |
|
519 } |
|
520 |
|
521 DavSession *sn = dav_session_new_auth(ctx, repo->url.value.ptr, user, password); |
|
522 if(password) { |
|
523 free(password); |
|
524 } |
|
525 |
|
526 sn->flags = dav_repository_get_flags(repo); |
|
527 sn->key = dav_context_get_key(ctx, repo->default_key.value.ptr); |
|
528 curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods); |
|
529 curl_easy_setopt(sn->handle, CURLOPT_SSLVERSION, repo->ssl_version); |
|
530 if(repo->cert.value.ptr) { |
|
531 curl_easy_setopt(sn->handle, CURLOPT_CAINFO, repo->cert.value.ptr); |
|
532 } |
|
533 if(!repo->verification.value) { |
|
534 curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYPEER, 0); |
|
535 curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYHOST, 0); |
|
536 } |
|
537 |
|
538 return sn; |
|
539 } |
|