Check-in [4df8c856ee]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Rework the "permissive-manifest-parser" idea to be simpler and to call it "strict-manifest-syntax".
Timelines: family | ancestors | descendants | both | sec2020
Files: files | file ages | folders
SHA3-256: 4df8c856ee7201b988e05b6c4b86f936344bc9ef63cc3e87fa1dfb8b8450e6b5
User & Date: drh 2020-08-18 19:49:13.224
Context
2020-08-18
19:56
Add a security audit warning if the strict-manifest-syntax flag is switched off. check-in: 3105bedff2 user: drh tags: sec2020
19:49
Rework the "permissive-manifest-parser" idea to be simpler and to call it "strict-manifest-syntax". check-in: 4df8c856ee user: drh tags: sec2020
14:02
Merge in the latest trunk changes. check-in: 917917aa55 user: drh tags: sec2020
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/file.c.
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
    }
  }
}

/*
** COMMAND: test-is-reserved-name
**
** Usage: %fossil test-is-ckout-db FILENAMES...
**
** Passes each given name to file_is_reserved_name() and outputs one
** line per file: the result value of that function followed by the
** name.
*/
void test_is_reserved_name_cmd(void){
  int i;







|







2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
    }
  }
}

/*
** COMMAND: test-is-reserved-name
**
** Usage: %fossil test-is-reserved-name FILENAMES...
**
** Passes each given name to file_is_reserved_name() and outputs one
** line per file: the result value of that function followed by the
** name.
*/
void test_is_reserved_name_cmd(void){
  int i;
Changes to src/main.c.
218
219
220
221
222
223
224


225
226
227
228
229
230
231
#endif
  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */



  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */
  char *zIpAddr;          /* The remote IP address */
  char *zNonce;           /* The nonce used for login */

  /* permissions available to current user */







>
>







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#endif
  int useLocalauth;       /* No login required if from 127.0.0.1 */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */
  int isHuman;            /* True if access by a human, not a spider or bot */
  int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
                          ** accessed through get_comment_format(). */
  int manifestStrict;     /* Whether or not to do strict enforcement of 
                          ** manifest syntax.  0 = unknown. 1 = no  2 = yes */

  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */
  char *zIpAddr;          /* The remote IP address */
  char *zNonce;           /* The nonce used for login */

  /* permissions available to current user */
Changes to src/manifest.c.
396
397
398
399
400
401
402







































403
404
405
406
407
408
409
** Frees all memory owned by the manifest "has-seen" cache.  Intended
** to be called only from the app's atexit() handler.
*/
void manifest_clear_cache(){
  bag_clear(&seenManifests);
}








































/*
** Parse a blob into a Manifest object.  The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return a pointer to an allocated Manifest object if the content







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
** Frees all memory owned by the manifest "has-seen" cache.  Intended
** to be called only from the app's atexit() handler.
*/
void manifest_clear_cache(){
  bag_clear(&seenManifests);
}


/*
** SETTING: strict-manifest-syntax  boolean default=on sensitive
** LEAVE THIS SETTING TURNED ON!
**
** This flag indicates that manifest syntax should be strictly enforced.
** It defaults to on.  Clearing this flag is a security risk.
**
** Some questionable constructs were allowed in manifests in historical
** versions of Fossil.  In particular, it was formerly allowed to
** include names like "_FOSSIL_" or ".fslckout" in subdirectories.  But
** doing so can lead to problems, and so newer versions of Fossil disallow
** that.
**
** This flag allows the older questionable constructs to appear in
** manifests for backwards compatibility for the very rare repositories
** that make use of the questionable behavior.
*/

/*
** Return true if manifest parsing rules are strictly enforced.  Return
** zero is certain questionable constructs should be allowed for legacy
** compatibility.
**
** At the current time, the only questionable construct that this applies
** to is the use of filenames like "_FOSSIL_" or ".fslckout" in subdirectories
** of the repository.  These names have never been allowed in the top-level
** directory, but historical versions of fossil allowed them in subdirectories.
**
** This routine is only called if a questionable construct is encountered,
** which is to say it is rarely called.
*/
int manifest_strict_enforcement(void){
  if( g.manifestStrict==0 ){
    g.manifestStrict = db_get_boolean("strict-manifest-syntax",1) + 1;
  }
  return g.manifestStrict - 1;
}

/*
** Parse a blob into a Manifest object.  The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed.  Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
** Return a pointer to an allocated Manifest object if the content
630
631
632
633
634
635
636
637

638
639
640
641
642
643
644
      case 'F': {
        char *zName, *zPerm, *zPriorName;
        zName = next_token(&x,0);
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname_nonstrict(zName) ){
          SYNTAX("F-card filename is not a simple path");
        }else if( file_is_reserved_name(zName,-1) ){

          SYNTAX("F-card contains a reserved name");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( zUuid==0 ) SYNTAX("missing hash on F-card");
          if( !hname_validate(zUuid,sz) ){
            SYNTAX("F-card hash invalid");







|
>







669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
      case 'F': {
        char *zName, *zPerm, *zPriorName;
        zName = next_token(&x,0);
        if( zName==0 ) SYNTAX("missing filename on F-card");
        defossilize(zName);
        if( !file_is_simple_pathname_nonstrict(zName) ){
          SYNTAX("F-card filename is not a simple path");
        }else if( file_is_reserved_name(zName,-1) 
               && manifest_strict_enforcement() ){
          SYNTAX("F-card contains a reserved name");
        }
        zUuid = next_token(&x, &sz);
        if( p->zBaseline==0 || zUuid!=0 ){
          if( zUuid==0 ) SYNTAX("missing hash on F-card");
          if( !hname_validate(zUuid,sz) ){
            SYNTAX("F-card hash invalid");