Fossil

Check-in [ce7baa97]
Login

Check-in [ce7baa97]

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

Overview
Comment:Avoid another attack vector when using SSH sync protocol by not calling a shell interpreter. Fixes only Unix-like environments by using execvp() instead of a string that can be mishandled by /bin/sh. Superseded by [3b191c984b] &co.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | ssh-shell-cleanup
Files: files | file ages | folders
SHA3-256: ce7baa9798de21aabfea96f80e0f61597eda7e989bc98b7a7df8cd23d63cdff9
User & Date: andybradford 2017-08-12 16:20:18
Original Comment: Avoid another attack vector when using SSH sync protocol by not calling a shell interpreter. Fixes only Unix-like environments by using execvp() instead of a string that can be mishandled by /bin/sh.
Context
2017-08-12
16:20
Avoid another attack vector when using SSH sync protocol by not calling a shell interpreter. Fixes only Unix-like environments by using execvp() instead of a string that can be mishandled by /bin/sh. Superseded by [3b191c984b] &co. ... (Closed-Leaf check-in: ce7baa97 user: andybradford tags: ssh-shell-cleanup)
04:19
Typo correction ... (check-in: 45a3d4b1 user: andygoth tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/http_transport.c.

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
157
/*
** SSH initialization of the transport layer
*/
int transport_ssh_open(UrlData *pUrlData){
  /* For SSH we need to create and run SSH fossil http
  ** to talk to the remote machine.
  */
  char *zSsh;        /* The base SSH command */
  Blob zCmd;         /* The SSH command */
  char *zHost;       /* The host name to contact */
  int n;             /* Size of prefix string */


  socket_ssh_resolve_addr(pUrlData);
  zSsh = db_get("ssh-command", zDefaultSshCmd);
  blob_init(&zCmd, zSsh, -1);

  if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
#ifdef _WIN32
    blob_appendf(&zCmd, " -P %d", pUrlData->port);
#else
    blob_appendf(&zCmd, " -p %d", pUrlData->port);
#endif
  }
  if( g.fSshTrace ){
    fossil_force_newline();
    fossil_print("%s", blob_str(&zCmd));  /* Show the base of the SSH command */
  }
  if( pUrlData->user && pUrlData->user[0] ){
    zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
  }else{
    zHost = mprintf("%s", pUrlData->name);
  }
  n = blob_size(&zCmd);
  blob_append(&zCmd, " ", 1);
  shell_escape(&zCmd, stripLeadingMinus(zHost));
  blob_append(&zCmd, " ", 1);
  shell_escape(&zCmd, mprintf("%s", pUrlData->fossil));
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append(&zCmd, " ", 1);

    shell_escape(&zCmd, mprintf("%s", stripLeadingMinus(pUrlData->path)));
  }
  if( g.fSshTrace ){

    fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
  }


  free(zHost);
  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
  if( sshPid==0 ){



    socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
  }
  blob_reset(&zCmd);
  return sshPid==0;
}

/*
** Open a connection to the server.  The server is defined by the following
** variables:
**







|
|


>


|
|
>


|

|

<
<
|
<






<
<
|
<
|
|

<
>
|


>
|
|
>
>

|

>
>
>
|

|







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
157
158
159
/*
** SSH initialization of the transport layer
*/
int transport_ssh_open(UrlData *pUrlData){
  /* For SSH we need to create and run SSH fossil http
  ** to talk to the remote machine.
  */
  Blob sshBase;      /* The base SSH command */
  Blob aArgv[50];    /* The SSH arguments array */
  char *zHost;       /* The host name to contact */
  int n;             /* Size of prefix string */
  int nToken, i;

  socket_ssh_resolve_addr(pUrlData);
  blob_init(&sshBase, db_get("ssh-command", zDefaultSshCmd), -1);
  blobarray_zero(aArgv, count(aArgv));
  n = nToken = blob_tokenize(&sshBase, aArgv, count(aArgv));
  if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
#ifdef _WIN32
    blob_append(&aArgv[nToken++], "-P", 2);
#else
    blob_append(&aArgv[nToken++], "-p", 2);
#endif


    blob_appendf(&aArgv[nToken++], "%d", pUrlData->port);

  }
  if( pUrlData->user && pUrlData->user[0] ){
    zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
  }else{
    zHost = mprintf("%s", pUrlData->name);
  }


  shell_escape(&aArgv[nToken++], stripLeadingMinus(zHost));

  shell_escape(&aArgv[nToken++], mprintf("%s", pUrlData->fossil));
  blob_append(&aArgv[nToken++], "test-http", 9);
  if( pUrlData->path && pUrlData->path[0] ){

    shell_escape(&aArgv[nToken++], 
                 mprintf("%s", stripLeadingMinus(pUrlData->path)));
  }
  if( g.fSshTrace ){
    for(i=0; i<nToken; i++){
      fossil_print("%s ", blob_str(&aArgv[i]));
    }
    fossil_force_newline();
  }
  free(zHost);
  popen2(&aArgv[0], nToken, &sshIn, &sshOut, &sshPid);
  if( sshPid==0 ){
    for(i=n; i<nToken; i++){
      blob_appendf(&sshBase, " %b", &aArgv[i]);
    }
    socket_set_errmsg("cannot start ssh tunnel using [%b]", &sshBase);
  }
  blobarray_reset(aArgv, nToken);
  return sshPid==0;
}

/*
** Open a connection to the server.  The server is defined by the following
** variables:
**

Changes to src/popen.c.

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
    win32_fatal_error("cannot create child process");
  }
  return rc!=0;
}
#endif

/*
** Create a child process running shell command "zCmd".  *ppOut is
** a FILE that becomes the standard input of the child process.
** (The caller writes to *ppOut in order to send text to the child.)
** *ppIn is stdout from the child process.  (The caller
** reads from *ppIn in order to receive input from the child.)
** Note that *ppIn is an unbuffered file descriptor, not a FILE.
** The process ID of the child is written into *pChildPid.
**
** Return the number of errors.
*/
int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){
#ifdef _WIN32
  HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
  SECURITY_ATTRIBUTES saAttr;
  DWORD childPid = 0;

  int fd;

  saAttr.nLength = sizeof(saAttr);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL;
  hStderr = GetStdHandle(STD_ERROR_HANDLE);
  if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
    win32_fatal_error("cannot create pipe for stdout");
  }
  SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);

  if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
    win32_fatal_error("cannot create pipe for stdin");
  }
  SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);






  win32_create_child_process(fossil_utf8_to_unicode(zCmd),
                             hStdinRd, hStdoutWr, hStderr,&childPid);
  *pChildPid = childPid;
  *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
  fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
  *ppOut = _fdopen(fd, "w");
  CloseHandle(hStdinRd);
  CloseHandle(hStdoutWr);







|









|




>
|















>
>
>
>
>
|







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
157
158
159
160
    win32_fatal_error("cannot create child process");
  }
  return rc!=0;
}
#endif

/*
** Create a child process from array of arguments in "aArgv".  *ppOut is
** a FILE that becomes the standard input of the child process.
** (The caller writes to *ppOut in order to send text to the child.)
** *ppIn is stdout from the child process.  (The caller
** reads from *ppIn in order to receive input from the child.)
** Note that *ppIn is an unbuffered file descriptor, not a FILE.
** The process ID of the child is written into *pChildPid.
**
** Return the number of errors.
*/
int popen2(Blob *aArgv, int nToken, int *pfdIn, FILE **ppOut, int *pChildPid){
#ifdef _WIN32
  HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
  SECURITY_ATTRIBUTES saAttr;
  DWORD childPid = 0;
  Blob shellcmd;
  int fd, i;

  saAttr.nLength = sizeof(saAttr);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL;
  hStderr = GetStdHandle(STD_ERROR_HANDLE);
  if( !CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 4096) ){
    win32_fatal_error("cannot create pipe for stdout");
  }
  SetHandleInformation( hStdoutRd, HANDLE_FLAG_INHERIT, FALSE);

  if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
    win32_fatal_error("cannot create pipe for stdin");
  }
  SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);

  blob_zero(&shellcmd);
  for(i=0; i<nToken-1; i++){
    blob_appendf(&shellcmd, "%b ", aArgv[i]);
  }
  blob_trim(&shellcmd);
  win32_create_child_process(fossil_utf8_to_unicode(blob_str(&shellcmd)),
                             hStdinRd, hStdoutWr, hStderr,&childPid);
  *pChildPid = childPid;
  *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
  fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
  *ppOut = _fdopen(fd, "w");
  CloseHandle(hStdinRd);
  CloseHandle(hStdoutWr);
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193





194
195
196
197
198
199
200
201
    close(pout[0]);
    close(pout[1]);
    *pChildPid = 0;
    return 1;
  }
  signal(SIGPIPE,SIG_IGN);
  if( *pChildPid==0 ){

    int fd;
    int nErr = 0;
    /* This is the child process */
    close(0);
    fd = dup(pout[0]);
    if( fd!=0 ) nErr++;
    close(pout[0]);
    close(pout[1]);
    close(1);
    fd = dup(pin[1]);
    if( fd!=1 ) nErr++;
    close(pin[0]);
    close(pin[1]);





    execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
    return 1;
  }else{
    /* This is the parent process */
    close(pin[1]);
    *pfdIn = pin[0];
    close(pout[0]);
    *ppOut = fdopen(pout[1], "w");







>
|












>
>
>
>
>
|







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
    close(pout[0]);
    close(pout[1]);
    *pChildPid = 0;
    return 1;
  }
  signal(SIGPIPE,SIG_IGN);
  if( *pChildPid==0 ){
    char **pzArgs = 0;
    int fd, i;
    int nErr = 0;
    /* This is the child process */
    close(0);
    fd = dup(pout[0]);
    if( fd!=0 ) nErr++;
    close(pout[0]);
    close(pout[1]);
    close(1);
    fd = dup(pin[1]);
    if( fd!=1 ) nErr++;
    close(pin[0]);
    close(pin[1]);
    pzArgs = fossil_malloc( (nToken+1)*sizeof(pzArgs[0]) );
    for(i=0; i<nToken; i++){
      pzArgs[i] = blob_str(&aArgv[i]);
    }
    pzArgs[i] = 0;
    execvp(pzArgs[0], pzArgs);
    return 1;
  }else{
    /* This is the parent process */
    close(pin[1]);
    *pfdIn = pin[0];
    close(pout[0]);
    *ppOut = fdopen(pout[1], "w");