author | Lars Hjemli <hjemli@gmail.com> | 2011-06-12 20:49:35 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2011-06-12 21:21:30 (UTC) |
commit | 7f88d20823ad9d375900657334bc27793860f6ee (patch) (unidiff) | |
tree | c9f9a0048cae2d94e97138e9ea82e2a103b215ad | |
parent | 2a8f553163d642e60092ced20631e1020581273b (diff) | |
download | cgit-7f88d20823ad9d375900657334bc27793860f6ee.zip cgit-7f88d20823ad9d375900657334bc27793860f6ee.tar.gz cgit-7f88d20823ad9d375900657334bc27793860f6ee.tar.bz2 |
ui-plain.c: fix html and links generated by print_dir() and print_dir_entry()
This patch fixes the following issues:
* the base argument usually isn't zero-terminated, so printing base
without considering baselen will usually generate random garbage
* when the current url represents a directory but doesn't end in a slash,
relative urls would be incorrect
* using unescaped paths allows XSS
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | ui-plain.c | 65 |
1 files changed, 46 insertions, 19 deletions
@@ -31,116 +31,143 @@ static void print_object(const unsigned char *sha1, const char *path) | |||
31 | html_status(404, "Not found", 0); | 31 | html_status(404, "Not found", 0); |
32 | return; | 32 | return; |
33 | } | 33 | } |
34 | ctx.page.mimetype = NULL; | 34 | ctx.page.mimetype = NULL; |
35 | ext = strrchr(path, '.'); | 35 | ext = strrchr(path, '.'); |
36 | if (ext && *(++ext)) { | 36 | if (ext && *(++ext)) { |
37 | mime = string_list_lookup(&ctx.cfg.mimetypes, ext); | 37 | mime = string_list_lookup(&ctx.cfg.mimetypes, ext); |
38 | if (mime) | 38 | if (mime) |
39 | ctx.page.mimetype = (char *)mime->util; | 39 | ctx.page.mimetype = (char *)mime->util; |
40 | } | 40 | } |
41 | if (!ctx.page.mimetype) { | 41 | if (!ctx.page.mimetype) { |
42 | if (buffer_is_binary(buf, size)) | 42 | if (buffer_is_binary(buf, size)) |
43 | ctx.page.mimetype = "application/octet-stream"; | 43 | ctx.page.mimetype = "application/octet-stream"; |
44 | else | 44 | else |
45 | ctx.page.mimetype = "text/plain"; | 45 | ctx.page.mimetype = "text/plain"; |
46 | } | 46 | } |
47 | ctx.page.filename = fmt("%s", path); | 47 | ctx.page.filename = fmt("%s", path); |
48 | ctx.page.size = size; | 48 | ctx.page.size = size; |
49 | ctx.page.etag = sha1_to_hex(sha1); | 49 | ctx.page.etag = sha1_to_hex(sha1); |
50 | cgit_print_http_headers(&ctx); | 50 | cgit_print_http_headers(&ctx); |
51 | html_raw(buf, size); | 51 | html_raw(buf, size); |
52 | match = 1; | 52 | match = 1; |
53 | } | 53 | } |
54 | 54 | ||
55 | static void print_dir(const unsigned char *sha1, const char *path, | 55 | static char *buildpath(const char *base, int baselen, const char *path) |
56 | const char *base) | ||
57 | { | 56 | { |
58 | char *fullpath; | 57 | if (path[0]) |
59 | if (path[0] || base[0]) | 58 | return fmt("%.*s%s/", baselen, base, path); |
60 | fullpath = fmt("/%s%s/", base, path); | ||
61 | else | 59 | else |
62 | fullpath = "/"; | 60 | return fmt("%.*s/", baselen, base); |
61 | } | ||
62 | |||
63 | static void print_dir(const unsigned char *sha1, const char *base, | ||
64 | int baselen, const char *path) | ||
65 | { | ||
66 | char *fullpath, *slash; | ||
67 | size_t len; | ||
68 | |||
69 | fullpath = buildpath(base, baselen, path); | ||
70 | slash = (fullpath[0] == '/' ? "" : "/"); | ||
63 | ctx.page.etag = sha1_to_hex(sha1); | 71 | ctx.page.etag = sha1_to_hex(sha1); |
64 | cgit_print_http_headers(&ctx); | 72 | cgit_print_http_headers(&ctx); |
65 | htmlf("<html><head><title>%s</title></head>\n<body>\n" | 73 | htmlf("<html><head><title>%s", slash); |
66 | " <h2>%s</h2>\n <ul>\n", fullpath, fullpath); | 74 | html_txt(fullpath); |
67 | if (path[0] || base[0]) | 75 | htmlf("</title></head>\n<body>\n<h2>%s", slash); |
68 | html(" <li><a href=\"../\">../</a></li>\n"); | 76 | html_txt(fullpath); |
77 | html("</h2>\n<ul>\n"); | ||
78 | len = strlen(fullpath); | ||
79 | if (len > 1) { | ||
80 | fullpath[len - 1] = 0; | ||
81 | slash = strrchr(fullpath, '/'); | ||
82 | if (slash) | ||
83 | *(slash + 1) = 0; | ||
84 | else | ||
85 | fullpath = NULL; | ||
86 | html("<li>"); | ||
87 | cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1, | ||
88 | fullpath); | ||
89 | html("</li>\n"); | ||
90 | } | ||
69 | match = 2; | 91 | match = 2; |
70 | } | 92 | } |
71 | 93 | ||
72 | static void print_dir_entry(const unsigned char *sha1, const char *path, | 94 | static void print_dir_entry(const unsigned char *sha1, const char *base, |
73 | unsigned mode) | 95 | int baselen, const char *path, unsigned mode) |
74 | { | 96 | { |
75 | const char *sep = ""; | 97 | char *fullpath; |
76 | if (S_ISDIR(mode)) | 98 | |
77 | sep = "/"; | 99 | fullpath = buildpath(base, baselen, path); |
78 | htmlf(" <li><a href=\"%s%s\">%s%s</a></li>\n", path, sep, path, sep); | 100 | if (!S_ISDIR(mode)) |
101 | fullpath[strlen(fullpath) - 1] = 0; | ||
102 | html(" <li>"); | ||
103 | cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, | ||
104 | fullpath); | ||
105 | html("</li>\n"); | ||
79 | match = 2; | 106 | match = 2; |
80 | } | 107 | } |
81 | 108 | ||
82 | static void print_dir_tail(void) | 109 | static void print_dir_tail(void) |
83 | { | 110 | { |
84 | html(" </ul>\n</body></html>\n"); | 111 | html(" </ul>\n</body></html>\n"); |
85 | } | 112 | } |
86 | 113 | ||
87 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, | 114 | static int walk_tree(const unsigned char *sha1, const char *base, int baselen, |
88 | const char *pathname, unsigned mode, int stage, | 115 | const char *pathname, unsigned mode, int stage, |
89 | void *cbdata) | 116 | void *cbdata) |
90 | { | 117 | { |
91 | if (baselen == match_baselen) { | 118 | if (baselen == match_baselen) { |
92 | if (S_ISREG(mode)) | 119 | if (S_ISREG(mode)) |
93 | print_object(sha1, pathname); | 120 | print_object(sha1, pathname); |
94 | else if (S_ISDIR(mode)) { | 121 | else if (S_ISDIR(mode)) { |
95 | print_dir(sha1, pathname, base); | 122 | print_dir(sha1, base, baselen, pathname); |
96 | return READ_TREE_RECURSIVE; | 123 | return READ_TREE_RECURSIVE; |
97 | } | 124 | } |
98 | } | 125 | } |
99 | else if (baselen > match_baselen) | 126 | else if (baselen > match_baselen) |
100 | print_dir_entry(sha1, pathname, mode); | 127 | print_dir_entry(sha1, base, baselen, pathname, mode); |
101 | else if (S_ISDIR(mode)) | 128 | else if (S_ISDIR(mode)) |
102 | return READ_TREE_RECURSIVE; | 129 | return READ_TREE_RECURSIVE; |
103 | 130 | ||
104 | return 0; | 131 | return 0; |
105 | } | 132 | } |
106 | 133 | ||
107 | static int basedir_len(const char *path) | 134 | static int basedir_len(const char *path) |
108 | { | 135 | { |
109 | char *p = strrchr(path, '/'); | 136 | char *p = strrchr(path, '/'); |
110 | if (p) | 137 | if (p) |
111 | return p - path + 1; | 138 | return p - path + 1; |
112 | return 0; | 139 | return 0; |
113 | } | 140 | } |
114 | 141 | ||
115 | void cgit_print_plain(struct cgit_context *ctx) | 142 | void cgit_print_plain(struct cgit_context *ctx) |
116 | { | 143 | { |
117 | const char *rev = ctx->qry.sha1; | 144 | const char *rev = ctx->qry.sha1; |
118 | unsigned char sha1[20]; | 145 | unsigned char sha1[20]; |
119 | struct commit *commit; | 146 | struct commit *commit; |
120 | const char *paths[] = {ctx->qry.path, NULL}; | 147 | const char *paths[] = {ctx->qry.path, NULL}; |
121 | 148 | ||
122 | if (!rev) | 149 | if (!rev) |
123 | rev = ctx->qry.head; | 150 | rev = ctx->qry.head; |
124 | 151 | ||
125 | if (get_sha1(rev, sha1)) { | 152 | if (get_sha1(rev, sha1)) { |
126 | html_status(404, "Not found", 0); | 153 | html_status(404, "Not found", 0); |
127 | return; | 154 | return; |
128 | } | 155 | } |
129 | commit = lookup_commit_reference(sha1); | 156 | commit = lookup_commit_reference(sha1); |
130 | if (!commit || parse_commit(commit)) { | 157 | if (!commit || parse_commit(commit)) { |
131 | html_status(404, "Not found", 0); | 158 | html_status(404, "Not found", 0); |
132 | return; | 159 | return; |
133 | } | 160 | } |
134 | if (!paths[0]) { | 161 | if (!paths[0]) { |
135 | paths[0] = ""; | 162 | paths[0] = ""; |
136 | match_baselen = -1; | 163 | match_baselen = -1; |
137 | print_dir(commit->tree->object.sha1, "", ""); | 164 | print_dir(commit->tree->object.sha1, "", 0, ""); |
138 | } | 165 | } |
139 | else | 166 | else |
140 | match_baselen = basedir_len(paths[0]); | 167 | match_baselen = basedir_len(paths[0]); |
141 | read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL); | 168 | read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL); |
142 | if (!match) | 169 | if (!match) |
143 | html_status(404, "Not found", 0); | 170 | html_status(404, "Not found", 0); |
144 | else if (match == 2) | 171 | else if (match == 2) |
145 | print_dir_tail(); | 172 | print_dir_tail(); |
146 | } | 173 | } |