dav/config.c

changeset 734
b2cd82149116
parent 732
b0eb645cd26e
child 735
74a6e2d4fb1f
equal deleted inserted replaced
733:a7883961b5f4 734:b2cd82149116
35 #include <libxml/tree.h> 35 #include <libxml/tree.h>
36 36
37 #include "pwd.h" 37 #include "pwd.h"
38 #include "config.h" 38 #include "config.h"
39 #include "main.h" 39 #include "main.h"
40 #include "pwd.h"
40 #include <libidav/utils.h> 41 #include <libidav/utils.h>
41 42
42 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) 43 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
43 #define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b) 44 #define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)
44 45
815 char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt"); 816 char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
816 int ret = pwdstore_store(pwdstore, pwfile); 817 int ret = pwdstore_store(pwdstore, pwfile);
817 free(pwfile); 818 free(pwfile);
818 return ret; 819 return ret;
819 } 820 }
821
822
823
824
825 Repository* url2repo_s(sstr_t url, char **path) {
826 *path = NULL;
827
828 int s;
829 if(sstrprefix(url, SC("http://"))) {
830 s = 7;
831 } else if(sstrprefix(url, SC("https://"))) {
832 s = 8;
833 } else {
834 s = 1;
835 }
836
837 // split URL into repository and path
838 sstr_t r = sstrsubs(url, s);
839 sstr_t p = sstrchr(r, '/');
840 r = sstrsubsl(url, 0, url.length-p.length);
841 if(p.length == 0) {
842 p = sstrn("/", 1);
843 }
844
845 Repository *repo = get_repository(r);
846 if(repo) {
847 *path = sstrdup(p).ptr;
848 } else {
849 // TODO: who is responsible for freeing this repository?
850 // how can the callee know, if he has to call free()?
851 repo = calloc(1, sizeof(Repository));
852 repo->name = strdup("");
853 repo->decrypt_content = true;
854 repo->verification = true;
855 repo->authmethods = CURLAUTH_BASIC;
856 if(url.ptr[url.length-1] == '/') {
857 repo->url = sstrdup(url).ptr;
858 *path = strdup("/");
859 } else if (sstrchr(url, '/').length > 0) {
860 // TODO: fix the following workaround after
861 // fixing the inconsistent behavior of util_url_*()
862 repo->url = util_url_base_s(url);
863 sstr_t truncated = sstrdup(url);
864 *path = strdup(util_url_path(truncated.ptr));
865 free(truncated.ptr);
866 } else {
867 repo->url = sstrdup(url).ptr;
868 *path = strdup("/");
869 }
870 }
871
872 return repo;
873 }
874
875 Repository* url2repo(char *url, char **path) {
876 return url2repo_s(sstr(url), path);
877 }
878
879 static int decrypt_secrets(CmdArgs *a, PwdStore *secrets) {
880 if(cmd_getoption(a, "noinput")) {
881 return 1;
882 }
883
884 char *ps_password = NULL;
885 if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) {
886 UcxBuffer *cmd_out = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
887 if(!util_exec_command(secrets->unlock_cmd, cmd_out)) {
888 // command successful, get first line from output without newline
889 // and use that as password for the secretstore
890 size_t len = 0;
891 for(size_t i=0;i<=cmd_out->size;i++) {
892 if(i == cmd_out->size || cmd_out->space[i] == '\n') {
893 len = i;
894 break;
895 }
896 }
897 if(len > 0) {
898 ps_password = malloc(len + 1);
899 memcpy(ps_password, cmd_out->space, len);
900 ps_password[len] = 0;
901 }
902 }
903 ucx_buffer_free(cmd_out);
904 }
905
906 if(!ps_password) {
907 ps_password = util_password_input("Master password: ");
908 if(!ps_password) {
909 return 1;
910 }
911 }
912
913 if(pwdstore_setpassword(secrets, ps_password)) {
914 fprintf(stderr, "Error: cannot create key from password\n");
915 return 1;
916 }
917 if(pwdstore_decrypt(secrets)) {
918 fprintf(stderr, "Error: cannot decrypt secrets store\n");
919 return 1;
920 }
921 return 0;
922 }
923
924 typedef struct CredLocation {
925 char *id;
926 char *location;
927 } CredLocation;
928
929 static int cmp_url_cred_entry(CredLocation *e1, CredLocation *e2, void *n) {
930 return strcmp(e2->location, e1->location);
931 }
932
933 static void free_cred_location(CredLocation *c) {
934 // c->id is not a copy, therefore we don't have to free it
935 free(c->location);
936 free(c);
937 }
938
939 static int get_stored_credentials(CmdArgs *a, char *credid, char **user, char **password) {
940 if(!credid) {
941 return 0;
942 }
943
944 PwdStore *secrets = get_pwdstore();
945 if(!secrets) {
946 fprintf(stderr, "Error: no secrets store available\n");
947 return 0;
948 }
949
950 if(pwdstore_has_id(secrets, credid)) {
951 if(!secrets->isdecrypted) {
952 if(decrypt_secrets(a, secrets)) {
953 return 0;
954 }
955 }
956
957 PwdEntry *s_cred = pwdstore_get(secrets, credid);
958 if(s_cred) {
959 *user = s_cred->user;
960 *password = s_cred->password;
961 return 1;
962 }
963 } else {
964 fprintf(stderr, "Error: credentials id '%s' not found\n", credid);
965 }
966
967 return 0;
968 }
969
970
971 static int get_location_credentials(CmdArgs *a, Repository *repo, char *path, char **user, char **password) {
972 PwdStore *secrets = get_pwdstore();
973 if(!secrets) {
974 return 0;
975 }
976
977 /*
978 * The list secrets->location contains urls or repo names as
979 * location strings. We need a list, that contains only urls
980 */
981 UcxList *locations = NULL;
982 UCX_FOREACH(elm, secrets->locations) {
983 PwdIndexEntry *e = elm->data;
984
985 UCX_FOREACH(loc, e->locations) {
986 char *path;
987 Repository *r = url2repo(loc->data, &path);
988 CredLocation *urlentry = calloc(1, sizeof(CredLocation));
989 urlentry->id = e->id;
990 urlentry->location = util_concat_path(r->url, path);
991 locations = ucx_list_append(locations, urlentry);
992 }
993 }
994 // the list must be sorted
995 locations = ucx_list_sort(locations, (cmp_func)cmp_url_cred_entry, NULL);
996
997 // create full request url string and remove protocol prefix
998 sstr_t req_url_proto = sstr(util_concat_path(repo->url, path));
999 sstr_t req_url = req_url_proto;
1000 if(sstrprefix(req_url, S("http://"))) {
1001 req_url = sstrsubs(req_url, 7);
1002 } else if(sstrprefix(req_url, S("https://"))) {
1003 req_url = sstrsubs(req_url, 8);
1004 }
1005
1006 // iterate over sorted locations and check if a location is a prefix
1007 // of the requested url
1008 char *id = NULL;
1009 int ret = 0;
1010 UCX_FOREACH(elm, locations) {
1011 CredLocation *cred = elm->data;
1012 sstr_t cred_url = sstr(cred->location);
1013
1014 // remove protocol prefix
1015 if(sstrprefix(cred_url, S("http://"))) {
1016 cred_url = sstrsubs(cred_url, 7);
1017 } else if(sstrprefix(cred_url, S("https://"))) {
1018 cred_url = sstrsubs(cred_url, 8);
1019 }
1020
1021 if(sstrprefix(req_url, cred_url)) {
1022 id = cred->id;
1023 break;
1024 }
1025 }
1026
1027 // if an id is found and we can access the decrypted secret store
1028 // we can set the user/password
1029 if(id && (secrets->isdecrypted || !decrypt_secrets(a, secrets))) {
1030 PwdEntry *cred = pwdstore_get(secrets, id);
1031 if(cred) {
1032 *user = cred->user;
1033 *password = cred->password;
1034 ret = 1;
1035 }
1036 }
1037
1038 free(req_url_proto.ptr);
1039 ucx_list_free_content(locations, (ucx_destructor)free_cred_location);
1040 ucx_list_free(locations);
1041
1042 return ret;
1043 }
1044
1045 DavSession* connect_to_repo(DavContext *ctx, Repository *repo, char *path, dav_auth_func authfunc, CmdArgs *a) {
1046 char *user = repo->user;
1047 char *password = repo->password;
1048
1049 if(!user && !password) {
1050 if(!get_stored_credentials(a, repo->stored_user, &user, &password)) {
1051 get_location_credentials(a, repo, path, &user, &password);
1052 }
1053 }
1054
1055 DavSession *sn = dav_session_new_auth(ctx, repo->url, user, password);
1056 sn->flags = get_repository_flags(repo);
1057 sn->key = dav_context_get_key(ctx, repo->default_key);
1058 curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods);
1059 curl_easy_setopt(sn->handle, CURLOPT_SSLVERSION, repo->ssl_version);
1060 if(repo->cert) {
1061 curl_easy_setopt(sn->handle, CURLOPT_CAINFO, repo->cert);
1062 }
1063 if(!repo->verification || cmd_getoption(a, "insecure")) {
1064 curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYPEER, 0);
1065 curl_easy_setopt(sn->handle, CURLOPT_SSL_VERIFYHOST, 0);
1066 }
1067 if(!cmd_getoption(a, "noinput")) {
1068 dav_session_set_authcallback(sn, authfunc, repo);
1069 }
1070 return sn;
1071 }
1072
1073 int request_auth(DavSession *sn, void *userdata) {
1074 Repository *repo = userdata;
1075
1076 char *user = NULL;
1077 char ubuf[256];
1078 if(repo->user) {
1079 user = repo->user;
1080 } else {
1081 fprintf(stderr, "User: ");
1082 fflush(stderr);
1083 user = fgets(ubuf, 256, stdin);
1084 }
1085 if(!user) {
1086 return 0;
1087 }
1088
1089 char *password = util_password_input("Password: ");
1090 if(!password || strlen(password) == 0) {
1091 return 0;
1092 }
1093
1094 size_t ulen = strlen(user);
1095 if(user[ulen-1] == '\n') {
1096 user[ulen-1] = '\0';
1097 }
1098
1099 dav_session_set_auth(sn, user, password);
1100 free(password);
1101
1102 return 0;
1103 }

mercurial