Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Improved detection of attempts to write through a symlink. Now also works for "revert", "stash", and "undo/redo". |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | sec2020 |
| Files: | files | file ages | folders |
| SHA3-256: |
f63297b2c521f48798158ead68cbc6d9 |
| User & Date: | drh 2020-08-19 12:08:45.770 |
Context
|
2020-08-19
| ||
| 12:58 | Merge additional symlink fixes. Back out comment-only changes from url.c. check-in: 0ea17c2b11 user: drh tags: sec2020-2.12-patch | |
| 12:22 | Additional defenses against doing "fossil add" of files that are beneath symlinks. check-in: 928b023cb7 user: drh tags: sec2020 | |
| 12:08 | Improved detection of attempts to write through a symlink. Now also works for "revert", "stash", and "undo/redo". check-in: f63297b2c5 user: drh tags: sec2020 | |
| 00:15 | Do not allow the "fossil add" command to add files beneath a symlink. check-in: a6abfb911b user: drh tags: sec2020 | |
Changes
Changes to src/file.c.
| ︙ | ︙ | |||
368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
}
z[j] = '/';
i = j;
}
fossil_free(z);
return 0;
}
/*
** Return 1 if zFilename is a directory. Return 0 if zFilename
** does not exist. Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename, int eFType){
| > > > > > > > > > > > > > > > > > > > > > > > > > | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
}
z[j] = '/';
i = j;
}
fossil_free(z);
return 0;
}
/*
** The file named zFile is suppose to be an in-tree file. Check to
** ensure that it will be safe to write to this file by verifying that
** there are no symlinks or other non-directory objects in between the
** root of the checkout and zFile.
**
** If a problem is found, print a warning message (using fossil_warning())
** and return non-zero. If everything is ok, return zero.
*/
int file_unsafe_in_tree_path(const char *zFile){
int n;
if( !file_is_absolute_path(zFile) ){
fossil_panic("%s is not an absolute pathname",zFile);
}
if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){
fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile);
}
n = file_nondir_objects_on_path(g.zLocalRoot, zFile);
if( n ){
fossil_warning("cannot write to %s because non-directory object %.*s"
" is in the way", zFile, n, zFile);
}
return n;
}
/*
** Return 1 if zFilename is a directory. Return 0 if zFilename
** does not exist. Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename, int eFType){
|
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
db_multi_exec("INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", zNew);
db_ephemeral_blob(&q, 6, &delta);
blob_write_to_file(&delta, zNPath);
file_setexe(zNPath, isExec);
}else if( isRemoved ){
fossil_print("DELETE %s\n", zOrig);
file_delete(zOPath);
}else{
Blob a, b, out, disk;
int isNewLink = file_islink(zOPath);
db_ephemeral_blob(&q, 6, &delta);
blob_read_from_file(&disk, zOPath, RepoFILE);
content_get(rid, &a);
blob_delta_apply(&a, &delta, &b);
| > > | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
db_multi_exec("INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", zNew);
db_ephemeral_blob(&q, 6, &delta);
blob_write_to_file(&delta, zNPath);
file_setexe(zNPath, isExec);
}else if( isRemoved ){
fossil_print("DELETE %s\n", zOrig);
file_delete(zOPath);
}else if( file_unsafe_in_tree_path(zNPath) ){
/* Ignore the unsafe path */
}else{
Blob a, b, out, disk;
int isNewLink = file_islink(zOPath);
db_ephemeral_blob(&q, 6, &delta);
blob_read_from_file(&disk, zOPath, RepoFILE);
content_get(rid, &a);
blob_delta_apply(&a, &delta, &b);
|
| ︙ | ︙ |
Changes to src/undo.c.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 |
int new_exists;
int old_exe;
int new_exe;
int new_link;
int old_link;
Blob current;
Blob new;
| | > > | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
int new_exists;
int old_exe;
int new_exe;
int new_link;
int old_link;
Blob current;
Blob new;
zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
old_link = db_column_int(&q, 3);
new_exists = file_size(zFullname, RepoFILE)>=0;
new_link = file_islink(0);
if( new_exists ){
blob_read_from_file(¤t, zFullname, RepoFILE);
new_exe = file_isexe(0,0);
}else{
blob_zero(¤t);
new_exe = 0;
}
blob_zero(&new);
old_exists = db_column_int(&q, 1);
old_exe = db_column_int(&q, 2);
if( old_exists ){
db_ephemeral_blob(&q, 0, &new);
}
if( file_unsafe_in_tree_path(zFullname) ){
/* do nothign with this unsafe file */
}else if( old_exists ){
if( new_exists ){
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
}else{
fossil_print("NEW %s\n", zPathname);
}
if( new_exists && (new_link || old_link) ){
file_delete(zFullname);
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 |
db_multi_exec(
"UPDATE OR REPLACE vfile"
" SET pathname=origname, origname=NULL"
" WHERE pathname=%Q AND origname!=pathname;"
"DELETE FROM vfile WHERE pathname=%Q",
zFile, zFile
);
}else{
sqlite3_int64 mtime;
int rvChnged = 0;
int rvPerm = manifest_file_mperm(pRvFile);
/* Determine if reverted-to file is different than checked out file. */
if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
}
| > > > | 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 |
db_multi_exec(
"UPDATE OR REPLACE vfile"
" SET pathname=origname, origname=NULL"
" WHERE pathname=%Q AND origname!=pathname;"
"DELETE FROM vfile WHERE pathname=%Q",
zFile, zFile
);
}else if( file_unsafe_in_tree_path(zFull) ){
/* Ignore this file */
}else{
sqlite3_int64 mtime;
int rvChnged = 0;
int rvPerm = manifest_file_mperm(pRvFile);
int n;
/* Determine if reverted-to file is different than checked out file. */
if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
}
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
int n;
id = db_column_int(&q, 0);
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 2);
isExe = db_column_int(&q, 3);
isLink = db_column_int(&q, 4);
content_get(rid, &content);
if( file_is_the_same(&content, zName) ){
blob_reset(&content);
if( file_setexe(zName, isExe) ){
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
file_mtime(zName, RepoFILE), id);
}
| > > > | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
int n;
id = db_column_int(&q, 0);
zName = db_column_text(&q, 1);
rid = db_column_int(&q, 2);
isExe = db_column_int(&q, 3);
isLink = db_column_int(&q, 4);
if( file_unsafe_in_tree_path(zName) ){
continue;
}
content_get(rid, &content);
if( file_is_the_same(&content, zName) ){
blob_reset(&content);
if( file_setexe(zName, isExe) ){
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
file_mtime(zName, RepoFILE), id);
}
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 |
promptFlag = 0;
} else if( cReply!='y' && cReply!='Y' ){
blob_reset(&content);
continue;
}
}
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
| < < < < < < | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
promptFlag = 0;
} else if( cReply!='y' && cReply!='Y' ){
blob_reset(&content);
continue;
}
}
if( verbose ) fossil_print("%s\n", &zName[nRepos]);
if( file_isdir(zName, RepoFILE)==1 ){
/*TODO(dchest): remove directories? */
fossil_fatal("%s is directory, cannot overwrite", zName);
}
if( file_size(zName, RepoFILE)>=0 && (isLink || file_islink(0)) ){
file_delete(zName);
}
|
| ︙ | ︙ |