@@ -167,7 +167,8 @@ static ssize_t validate_path(const uint8_t *const untrusted_name,
167
167
return allow_non_canonical ? 0 : - ENOLINK ; // empty path
168
168
if (untrusted_name [0 ] == '/' )
169
169
return - ENOLINK ; // absolute path
170
- for (size_t i = 0 ; untrusted_name [i ]; i ++ ) {
170
+ size_t i ;
171
+ for (i = 0 ; untrusted_name [i ]; i ++ ) {
171
172
if (i == 0 || untrusted_name [i - 1 ] == '/' ) {
172
173
// Start of a path component
173
174
switch (untrusted_name [i ]) {
@@ -216,6 +217,14 @@ static ssize_t validate_path(const uint8_t *const untrusted_name,
216
217
}
217
218
}
218
219
}
220
+ if (i < 1 || untrusted_name [i ]) {
221
+ // ideally this would be COMPILETIME_UNREACHABLE but GCC can't prove this
222
+ assert (0 );
223
+ return - EILSEQ ;
224
+ }
225
+ if ((flags & QUBES_PURE_ALLOW_TRAILING_SLASH ) == 0 &&
226
+ untrusted_name [i - 1 ] == '/' )
227
+ return - EILSEQ ;
219
228
return non_dotdot_components ;
220
229
}
221
230
@@ -224,6 +233,7 @@ static bool flag_check(const uint32_t flags)
224
233
int const allowed = (QUBES_PURE_ALLOW_UNSAFE_CHARACTERS |
225
234
QUBES_PURE_ALLOW_NON_CANONICAL_SYMLINKS |
226
235
QUBES_PURE_ALLOW_NON_CANONICAL_PATHS |
236
+ QUBES_PURE_ALLOW_TRAILING_SLASH |
227
237
QUBES_PURE_ALLOW_UNSAFE_SYMLINKS );
228
238
return (flags & ~(__typeof__ (flags ))allowed ) == 0 ;
229
239
}
@@ -243,7 +253,8 @@ qubes_pure_validate_file_name_v2(const uint8_t *const untrusted_filename,
243
253
QUBES_PURE_PUBLIC bool
244
254
qubes_pure_validate_file_name (const uint8_t * const untrusted_filename )
245
255
{
246
- return qubes_pure_validate_file_name_v2 (untrusted_filename , 0 ) == 0 ;
256
+ return qubes_pure_validate_file_name_v2 (untrusted_filename ,
257
+ QUBES_PURE_ALLOW_TRAILING_SLASH ) == 0 ;
247
258
}
248
259
249
260
QUBES_PURE_PUBLIC int
@@ -271,16 +282,19 @@ qubes_pure_validate_symbolic_link_v2(const uint8_t *untrusted_name,
271
282
// (which resolves to "c"). Similarly and "a/b/c" can point to "../d"
272
283
// (which resolves to "a/d") but not "../../d" (which resolves to "d").
273
284
// This ensures that ~/QubesIncoming/QUBENAME/a/b cannot point outside
274
- // of ~/QubesIncoming/QUBENAME/a.
275
- ssize_t res = validate_path (untrusted_target , (size_t )(depth - 2 ), flags );
285
+ // of ~/QubesIncoming/QUBENAME/a. Always allow trailing slash in the
286
+ // symbolic link target, whether or not they are allowed in the path.
287
+ ssize_t res = validate_path (untrusted_target , (size_t )(depth - 2 ),
288
+ flags | QUBES_PURE_ALLOW_TRAILING_SLASH );
276
289
return res < 0 ? res : 0 ;
277
290
}
278
291
279
292
QUBES_PURE_PUBLIC bool
280
293
qubes_pure_validate_symbolic_link (const uint8_t * untrusted_name ,
281
294
const uint8_t * untrusted_target )
282
295
{
283
- return qubes_pure_validate_symbolic_link_v2 (untrusted_name , untrusted_target , 0 ) == 0 ;
296
+ return qubes_pure_validate_symbolic_link_v2 (untrusted_name , untrusted_target ,
297
+ QUBES_PURE_ALLOW_TRAILING_SLASH ) == 0 ;
284
298
}
285
299
286
300
QUBES_PURE_PUBLIC bool
0 commit comments