comparison mod_http_upload/mod_http_upload.lua @ 2680:96bf67f1f960

mod_http_upload: Validate that file extension (used by mod_http_files) matches mime type given by client
author Kim Alvefur <zash@zash.se>
date Thu, 13 Apr 2017 20:23:17 +0200
parents 2dec7cad9218
children 8d8ba28d020f
comparison
equal deleted inserted replaced
2679:5f60dd12dbb8 2680:96bf67f1f960
41 end 41 end
42 42
43 -- depends 43 -- depends
44 module:depends("http"); 44 module:depends("http");
45 module:depends("disco"); 45 module:depends("disco");
46
47 local http_files = module:depends("http_files");
48 local mime_map = module:shared("/*/http_files/mime").types;
46 49
47 -- namespaces 50 -- namespaces
48 local namespace = "urn:xmpp:http:upload:0"; 51 local namespace = "urn:xmpp:http:upload:0";
49 local legacy_namespace = "urn:xmpp:http:upload"; 52 local legacy_namespace = "urn:xmpp:http:upload";
50 53
98 sum = sum + item.size; 101 sum = sum + item.size;
99 end 102 end
100 return sum < quota; 103 return sum < quota;
101 end 104 end
102 105
103 local function handle_request(origin, stanza, xmlns, filename, filesize) 106 local function handle_request(origin, stanza, xmlns, filename, filesize, mimetype)
104 -- local clients only 107 -- local clients only
105 if origin.type ~= "c2s" then 108 if origin.type ~= "c2s" then
106 module:log("debug", "Request for upload slot from a %s", origin.type); 109 module:log("debug", "Request for upload slot from a %s", origin.type);
107 origin.send(st.error_reply(stanza, "cancel", "not-authorized")); 110 origin.send(st.error_reply(stanza, "cancel", "not-authorized"));
108 return true; 111 return true;
127 elseif not check_quota(origin.username, origin.host, filesize) then 130 elseif not check_quota(origin.username, origin.host, filesize) then
128 module:log("debug", "Upload of %dB by %s would exceed quota", filesize, origin.full_jid); 131 module:log("debug", "Upload of %dB by %s would exceed quota", filesize, origin.full_jid);
129 origin.send(st.error_reply(stanza, "wait", "resource-constraint", "Quota reached")); 132 origin.send(st.error_reply(stanza, "wait", "resource-constraint", "Quota reached"));
130 return true; 133 return true;
131 end 134 end
135
136 if mime_map then
137 local file_ext = filename:match("%.([^.]+)$");
138 if (not file_ext and mimetype ~= "application/octet-stream") or (file_ext and mime_map[file_ext] ~= mimetype) then
139 origin.send(st.error_reply(stanza, "modify", "bad-request", "MIME type does not match file extension"));
140 return true;
141 end
142 end
143
132 local reply = st.reply(stanza); 144 local reply = st.reply(stanza);
133 reply:tag("slot", { xmlns = xmlns }); 145 reply:tag("slot", { xmlns = xmlns });
134 146
135 local random_dir; 147 local random_dir;
136 repeat random_dir = uuid(); 148 repeat random_dir = uuid();
160 module:hook("iq/host/"..namespace..":request", function (event) 172 module:hook("iq/host/"..namespace..":request", function (event)
161 local stanza, origin = event.stanza, event.origin; 173 local stanza, origin = event.stanza, event.origin;
162 local request = stanza.tags[1]; 174 local request = stanza.tags[1];
163 local filename = request.attr.filename; 175 local filename = request.attr.filename;
164 local filesize = tonumber(request.attr.size); 176 local filesize = tonumber(request.attr.size);
165 return handle_request(origin, stanza, namespace, filename, filesize); 177 local mimetype = request.attr["content-type"];
178 return handle_request(origin, stanza, namespace, filename, filesize, mimetype);
166 end); 179 end);
167 180
168 module:hook("iq/host/"..legacy_namespace..":request", function (event) 181 module:hook("iq/host/"..legacy_namespace..":request", function (event)
169 local stanza, origin = event.stanza, event.origin; 182 local stanza, origin = event.stanza, event.origin;
170 local request = stanza.tags[1]; 183 local request = stanza.tags[1];
171 local filename = request:get_child_text("filename"); 184 local filename = request:get_child_text("filename");
172 local filesize = tonumber(request:get_child_text("size")); 185 local filesize = tonumber(request:get_child_text("size"));
173 return handle_request(origin, stanza, legacy_namespace, filename, filesize); 186 local mimetype = request:get_child_text("content-type");
187 return handle_request(origin, stanza, legacy_namespace, filename, filesize, mimetype);
174 end); 188 end);
175 189
176 -- http service 190 -- http service
177 local function upload_data(event, path) 191 local function upload_data(event, path)
178 local uploader = pending_slots[path]; 192 local uploader = pending_slots[path];
254 else 268 else
255 response.conn:close(); 269 response.conn:close();
256 end 270 end
257 end 271 end
258 272
259 local serve_uploaded_files = module:depends("http_files").serve(storage_path); 273 local serve_uploaded_files = http_files.serve(storage_path);
260 274
261 local function serve_head(event, path) 275 local function serve_head(event, path)
262 event.response.send = send_response_sans_body; 276 event.response.send = send_response_sans_body;
263 return serve_uploaded_files(event, path); 277 return serve_uploaded_files(event, path);
264 end 278 end