application/config.c

changeset 55
1ce14068ef31
parent 50
9c25e2616bfa
equal deleted inserted replaced
54:3ca3acefc66a 55:1ce14068ef31
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2024 Olaf Wintermann. All rights reserved. 4 * Copyright 2018 Olaf Wintermann. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
33 #include <cx/hash_map.h> 33 #include <cx/hash_map.h>
34 #include <cx/utils.h> 34 #include <cx/utils.h>
35 #include <errno.h> 35 #include <errno.h>
36 #include <libxml/tree.h> 36 #include <libxml/tree.h>
37 37
38 #include "config.h"
38 #include "pwd.h" 39 #include "pwd.h"
39 #include "config.h"
40 #include "system.h" 40 #include "system.h"
41 41
42 #include <libidav/utils.h> 42 #include <libidav/utils.h>
43 #include <libidav/config.h> 43 #include <libidav/config.h>
44 44
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 }

mercurial