Check-in [6d0083adce]
Not logged in

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

Overview
Comment:Show the parent-project-* CONFIG entries (if they exist) with the "fossil remote config-data" command. When parsing a URL, if the URL comes from the CONFIG table, remember the CONFIG table entry that supplied the password.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6d0083adce5597e106f50369522311d9576d8cff91ea8a380b8aac634fcbfaf7
User & Date: drh 2022-12-30 16:32:24.666
Context
2022-12-30
20:54
Security enhancement: Do not store the passwords for remote URLs directly, but instead store the sha1_shared_secret() encoding of those passwords. It is the SHA1 encoding that gets transmitted to the server anyhow, so we might as well just store that. The SHA1 encoding cannot be used to log in. The password is still protected using obscure() ev... check-in: 41ba6ea7db user: drh tags: trunk
16:32
Show the parent-project-* CONFIG entries (if they exist) with the "fossil remote config-data" command. When parsing a URL, if the URL comes from the CONFIG table, remember the CONFIG table entry that supplied the password. check-in: 6d0083adce user: drh tags: trunk
12:26
Fix minor typos in the diff source code. check-in: 4e169542ae user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/sync.c.
767
768
769
770
771
772
773
774


775
776
777
778
779
780
781


782
783
784
785
786
787
788
789
    ** Show the CONFIG table entries that relate to remembering remote URLs
    */
    Stmt q;
    int n;
    n = db_int(13,
       "SELECT max(length(name))"
       "  FROM config"
       " WHERE name GLOB 'sync-*:*' OR name GLOB 'last-sync-*'"


    );
    db_prepare(&q,
       "SELECT name,"
       "       CASE WHEN name LIKE '%%sync-pw%%'"
                  " THEN printf('%%.*c',length(value),'*') ELSE value END"
       "  FROM config"
       " WHERE name GLOB 'sync-*:*' OR name GLOB 'last-sync-*'"


       " ORDER BY name LIKE '%%sync-pw%%', name"
    );
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%-*s  %s\n",
        n, db_column_text(&q,0),
        db_column_text(&q,1)
      );
    }







|
>
>



|


|
>
>
|







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
    ** Show the CONFIG table entries that relate to remembering remote URLs
    */
    Stmt q;
    int n;
    n = db_int(13,
       "SELECT max(length(name))"
       "  FROM config"
       " WHERE name GLOB 'sync-*:*'"
          " OR name GLOB 'last-sync-*'"
          " OR name GLOB 'parent-project-*'"
    );
    db_prepare(&q,
       "SELECT name,"
       "       CASE WHEN name LIKE '%%sync-pw%%' OR name='parent-project-pw'"
                  " THEN printf('%%.*c',length(value),'*') ELSE value END"
       "  FROM config"
       " WHERE name GLOB 'sync-*:*'"
          " OR name GLOB 'last-sync-*'"
          " OR name GLOB 'parent-project-*'"
       " ORDER BY name LIKE '%%sync-pw%%' OR name='parent-project-pw', name"
    );
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%-*s  %s\n",
        n, db_column_text(&q,0),
        db_column_text(&q,1)
      );
    }
Changes to src/url.c.
59
60
61
62
63
64
65

66
67

68
69
70
71
72
73
74
75
76
77
  int dfltPort;         /* The default port for the given protocol */
  char *path;           /* Pathname for http: */
  char *user;           /* User id for http: */
  char *passwd;         /* Password for http: */
  char *canonical;      /* Canonical representation of the URL */
  char *proxyAuth;      /* Proxy-Authorizer: string */
  char *fossil;         /* The fossil query parameter on ssh: */

  unsigned flags;       /* Boolean flags controlling URL processing */
  int useProxy;         /* Used to remember that a proxy is in use */

  char *proxyUrlPath;   /* Remember path when proxy is use */
  char *proxyUrlCanonical; /* Remember canonical path when proxy is use */
  int proxyOrigPort;    /* Tunneled port number for https through proxy */
};
#endif /* INTERFACE */


/*
** Parse the URL in the zUrl argument. Store results in the pUrlData object.
** Populate members of pUrlData as follows:







>


>
|

<







59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
  int dfltPort;         /* The default port for the given protocol */
  char *path;           /* Pathname for http: */
  char *user;           /* User id for http: */
  char *passwd;         /* Password for http: */
  char *canonical;      /* Canonical representation of the URL */
  char *proxyAuth;      /* Proxy-Authorizer: string */
  char *fossil;         /* The fossil query parameter on ssh: */
  char *pwConfig;       /* CONFIG table entry that gave us the password */
  unsigned flags;       /* Boolean flags controlling URL processing */
  int useProxy;         /* Used to remember that a proxy is in use */
  int proxyOrigPort;       /* Tunneled port number for https through proxy */
  char *proxyUrlPath;      /* Remember path when proxy is use */
  char *proxyUrlCanonical; /* Remember canonical path when proxy is use */

};
#endif /* INTERFACE */


/*
** Parse the URL in the zUrl argument. Store results in the pUrlData object.
** Populate members of pUrlData as follows:
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
**      dfltPort    Default TCP port number (80 or 443).
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.
**      canonical   The URL in canonical form, omitting the password
**
** If zUrl==0 and URL_USE_CONFIG is set, then parse the URL stored
** in last-sync-url and last-sync-pw of the CONFIG table.  Or if 
** URL_USE_PARENT is also set, then use parent-project-url and
** parent-project-pw from the CONFIG table instead of last-sync-url
** and last-sync-pw.
**
** If zUrl is a symbolic name and URL_USE_CONFIG is true, then look up
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
** %Q is the symbolic name.
**
** This routine differs from url_parse() in that this routine stores the
** results in pUrlData and does not change the values of global variables.
** The url_parse() routine puts its result in g.url.
*/
void url_parse_local(
  const char *zUrl,
  unsigned int urlFlags,
  UrlData *pUrlData
){
  int i, j, c;
  char *zFile = 0;


  if( urlFlags & URL_USE_CONFIG ){
    if( zUrl==0 || strcmp(zUrl,"default")==0 ){
      const char *zPwConfig = "last-sync-pw";
      if( urlFlags & URL_USE_PARENT ){
        zUrl = db_get("parent-project-url", 0);
        if( zUrl==0 ){
          zUrl = db_get("last-sync-url",0);
        }else{
          zPwConfig = "parent-project-pw";
        }
      }else{
        zUrl = db_get("last-sync-url", 0);
      }
      if( zUrl==0 ) return;
      if( pUrlData->passwd==0 ){
        pUrlData->passwd = unobscure(db_get(zPwConfig, 0));

      }
      pUrlData->isAlias = 1;
    }else{
      char *zKey = sqlite3_mprintf("sync-url:%q", zUrl);
      char *zAlt = db_get(zKey, 0);
      sqlite3_free(zKey);
      if( zAlt ){

        pUrlData->passwd = unobscure(
          db_text(0, "SELECT value FROM config WHERE name='sync-pw:%q'",zUrl)
        );
        zUrl = zAlt;
        urlFlags |= URL_REMEMBER_PW;
        pUrlData->isAlias = 1;
      }else{
        pUrlData->isAlias = 0;
      }

    }
  }else{
    if( zUrl==0 ) return;
  }

  if( strncmp(zUrl, "http://", 7)==0
   || strncmp(zUrl, "https://", 8)==0







|
|




|















>
















>





<

>









>







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
**      dfltPort    Default TCP port number (80 or 443).
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.
**      canonical   The URL in canonical form, omitting the password
**
** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the
** URL stored in last-sync-url and last-sync-pw of the CONFIG table.  Or if 
** URL_USE_PARENT is also set, then use parent-project-url and
** parent-project-pw from the CONFIG table instead of last-sync-url
** and last-sync-pw.
**
** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
** %Q is the symbolic name.
**
** This routine differs from url_parse() in that this routine stores the
** results in pUrlData and does not change the values of global variables.
** The url_parse() routine puts its result in g.url.
*/
void url_parse_local(
  const char *zUrl,
  unsigned int urlFlags,
  UrlData *pUrlData
){
  int i, j, c;
  char *zFile = 0;

  pUrlData->pwConfig = 0;
  if( urlFlags & URL_USE_CONFIG ){
    if( zUrl==0 || strcmp(zUrl,"default")==0 ){
      const char *zPwConfig = "last-sync-pw";
      if( urlFlags & URL_USE_PARENT ){
        zUrl = db_get("parent-project-url", 0);
        if( zUrl==0 ){
          zUrl = db_get("last-sync-url",0);
        }else{
          zPwConfig = "parent-project-pw";
        }
      }else{
        zUrl = db_get("last-sync-url", 0);
      }
      if( zUrl==0 ) return;
      if( pUrlData->passwd==0 ){
        pUrlData->passwd = unobscure(db_get(zPwConfig, 0));
        pUrlData->pwConfig = fossil_strdup(zPwConfig);
      }
      pUrlData->isAlias = 1;
    }else{
      char *zKey = sqlite3_mprintf("sync-url:%q", zUrl);
      char *zAlt = db_get(zKey, 0);

      if( zAlt ){
        pUrlData->pwConfig = mprintf("sync-pw:%q", zUrl);
        pUrlData->passwd = unobscure(
          db_text(0, "SELECT value FROM config WHERE name='sync-pw:%q'",zUrl)
        );
        zUrl = zAlt;
        urlFlags |= URL_REMEMBER_PW;
        pUrlData->isAlias = 1;
      }else{
        pUrlData->isAlias = 0;
      }
      sqlite3_free(zKey);
    }
  }else{
    if( zUrl==0 ) return;
  }

  if( strncmp(zUrl, "http://", 7)==0
   || strncmp(zUrl, "https://", 8)==0
402
403
404
405
406
407
408

409
410
411
412
413
414
415
  }
  fossil_free(p->canonical);
  fossil_free(p->name);
  fossil_free(p->path);
  fossil_free(p->user);
  fossil_free(p->passwd);
  fossil_free(p->fossil);

  memset(p, 0, sizeof(*p));
}

/*
** Parse the given URL, which describes a sync server.  Populate variables
** in the global "g.url" structure as shown below.  If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password







>







406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
  }
  fossil_free(p->canonical);
  fossil_free(p->name);
  fossil_free(p->path);
  fossil_free(p->user);
  fossil_free(p->passwd);
  fossil_free(p->fossil);
  fossil_free(p->pwConfig);
  memset(p, 0, sizeof(*p));
}

/*
** Parse the given URL, which describes a sync server.  Populate variables
** in the global "g.url" structure as shown below.  If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
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

449


450
451
452
453
454


455
456
457
458
459
460




461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

476




477
478
479
480
481
482
483
**      g.url.port        TCP port number for HTTP or HTTPS.
**      g.url.dfltPort    Default TCP port number (80 or 443).
**      g.url.path        Path name for HTTP or HTTPS.
**      g.url.user        Userid.
**      g.url.passwd      Password.
**      g.url.hostname    HOST:PORT or just HOST if port is the default.
**      g.url.canonical   The URL in canonical form, omitting the password

**
** HTTP url format as follows (HTTPS is the same with a different scheme):
**
**     http://userid:password@host:port/path
**
** SSH url format is:
**
**     ssh://userid@host:port/path?fossil=path/to/fossil.exe
**









*/
void url_parse(const char *zUrl, unsigned int urlFlags){
  url_parse_local(zUrl, urlFlags, &g.url);
}

/*
** COMMAND: test-urlparser
**
** Usage: %fossil test-urlparser URL ?options?
**

**    --remember      Store results in last-sync-url


**    --prompt-pw     Prompt for password if missing
*/
void cmd_test_urlparser(void){
  int i;
  unsigned fg = 0;


  url_proxy_options();
  if( find_option("remember",0,0) ){
    db_must_be_within_tree();
    fg |= URL_REMEMBER;
  }
  if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW;




  if( g.argc!=3 && g.argc!=4 ){
    usage("URL");
  }
  url_parse(g.argv[2], fg);
  for(i=0; i<2; i++){
    fossil_print("g.url.isFile    = %d\n", g.url.isFile);
    fossil_print("g.url.isHttps   = %d\n", g.url.isHttps);
    fossil_print("g.url.isSsh     = %d\n", g.url.isSsh);
    fossil_print("g.url.protocol  = %s\n", g.url.protocol);
    fossil_print("g.url.name      = %s\n", g.url.name);
    fossil_print("g.url.port      = %d\n", g.url.port);
    fossil_print("g.url.dfltPort  = %d\n", g.url.dfltPort);
    fossil_print("g.url.hostname  = %s\n", g.url.hostname);
    fossil_print("g.url.path      = %s\n", g.url.path);
    fossil_print("g.url.user      = %s\n", g.url.user);

    fossil_print("g.url.passwd    = %s\n", g.url.passwd);




    fossil_print("g.url.canonical = %s\n", g.url.canonical);
    fossil_print("g.url.fossil    = %s\n", g.url.fossil);
    fossil_print("g.url.flags     = 0x%02x\n", g.url.flags);
    fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
    if( g.url.isFile || g.url.isSsh ) break;
    if( i==0 ){
      fossil_print("********\n");







>









>
>
>
>
>
>
>
>
>










>

>
>
|




>
>

|
<
<
<
|
>
>
>
>















>
|
>
>
>
>







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
**      g.url.port        TCP port number for HTTP or HTTPS.
**      g.url.dfltPort    Default TCP port number (80 or 443).
**      g.url.path        Path name for HTTP or HTTPS.
**      g.url.user        Userid.
**      g.url.passwd      Password.
**      g.url.hostname    HOST:PORT or just HOST if port is the default.
**      g.url.canonical   The URL in canonical form, omitting the password
**      g.url.pwConfig    Name of CONFIG table entry containing the password
**
** HTTP url format as follows (HTTPS is the same with a different scheme):
**
**     http://userid:password@host:port/path
**
** SSH url format is:
**
**     ssh://userid@host:port/path?fossil=path/to/fossil.exe
**
** If URL_USE_CONFIG is set then the URL and password might be pulled from
** the CONFIG table rather than from the zUrl parameter.  If zUrl is NULL
** or "default" then the URL is given by the "last-sync-url" setting and
** the password comes form the "last-sync-pw" setting.  If zUrl is a symbolic
** name, then the URL comes from "sync-url:NAME" and the password from
** "sync-pw:NAME" where NAME is the input zUrl string.  Whenever the
** password is taken from the CONFIG table, the g.url.pwConfig field is
** set to the CONFIG.NAME value from which that password is taken.  Otherwise,
** g.url.pwConfig is NULL.
*/
void url_parse(const char *zUrl, unsigned int urlFlags){
  url_parse_local(zUrl, urlFlags, &g.url);
}

/*
** COMMAND: test-urlparser
**
** Usage: %fossil test-urlparser URL ?options?
**
**    --prompt-pw     Prompt for password if missing
**    --remember      Store results in last-sync-url
**    --show-pw       Show the CONFIG-derived password in the output
**    --use-config    Pull URL and password from the CONFIG table
**    --use-parent    Use the parent project URL
*/
void cmd_test_urlparser(void){
  int i;
  unsigned fg = 0;
  int showPw = 0;
  db_must_be_within_tree();
  url_proxy_options();
  if( find_option("remember",0,0) )    fg |= URL_REMEMBER;



  if( find_option("prompt-pw",0,0) )   fg |= URL_PROMPT_PW;
  if( find_option("use-parent",0,0) )  fg |= URL_USE_PARENT|URL_USE_CONFIG;
  if( find_option("use-config",0,0) )  fg |= URL_USE_CONFIG;
  if( find_option("show-pw",0,0) )     showPw = 1;
  if( (fg & URL_USE_CONFIG)==0 )       showPw = 1;
  if( g.argc!=3 && g.argc!=4 ){
    usage("URL");
  }
  url_parse(g.argv[2], fg);
  for(i=0; i<2; i++){
    fossil_print("g.url.isFile    = %d\n", g.url.isFile);
    fossil_print("g.url.isHttps   = %d\n", g.url.isHttps);
    fossil_print("g.url.isSsh     = %d\n", g.url.isSsh);
    fossil_print("g.url.protocol  = %s\n", g.url.protocol);
    fossil_print("g.url.name      = %s\n", g.url.name);
    fossil_print("g.url.port      = %d\n", g.url.port);
    fossil_print("g.url.dfltPort  = %d\n", g.url.dfltPort);
    fossil_print("g.url.hostname  = %s\n", g.url.hostname);
    fossil_print("g.url.path      = %s\n", g.url.path);
    fossil_print("g.url.user      = %s\n", g.url.user);
    if( showPw || g.url.pwConfig==0 ){
      fossil_print("g.url.passwd    = %s\n", g.url.passwd);
    }else{
      fossil_print("g.url.passwd    = ************\n");
    }
    fossil_print("g.url.pwConfig  = %s\n", g.url.pwConfig);
    fossil_print("g.url.canonical = %s\n", g.url.canonical);
    fossil_print("g.url.fossil    = %s\n", g.url.fossil);
    fossil_print("g.url.flags     = 0x%02x\n", g.url.flags);
    fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
    if( g.url.isFile || g.url.isSsh ) break;
    if( i==0 ){
      fossil_print("********\n");
Changes to src/xfer.c.
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
** If anything fails to check out, no changes are made to privileges.
**
** Signature generation on the client side is handled by the
** http_exchange() routine.
**
** Return non-zero for a login failure and zero for success.
*/
int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
  Stmt q;
  int rc = -1;
  char *zLogin = blob_terminate(pLogin);
  defossilize(zLogin);

  if( fossil_strcmp(zLogin, "nobody")==0
   || fossil_strcmp(zLogin,"anonymous")==0







|







812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
** If anything fails to check out, no changes are made to privileges.
**
** Signature generation on the client side is handled by the
** http_exchange() routine.
**
** Return non-zero for a login failure and zero for success.
*/
static int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
  Stmt q;
  int rc = -1;
  char *zLogin = blob_terminate(pLogin);
  defossilize(zLogin);

  if( fossil_strcmp(zLogin, "nobody")==0
   || fossil_strcmp(zLogin,"anonymous")==0