Fossil SCM

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.

andybradford 2017-08-12 16:20 trunk
Commit ce7baa9798de21aabfea96f80e0f61597eda7e989bc98b7a7df8cd23d63cdff9
2 files changed +24 -22 +18 -6
--- src/http_transport.c
+++ src/http_transport.c
@@ -103,53 +103,55 @@
103103
*/
104104
int transport_ssh_open(UrlData *pUrlData){
105105
/* For SSH we need to create and run SSH fossil http
106106
** to talk to the remote machine.
107107
*/
108
- char *zSsh; /* The base SSH command */
109
- Blob zCmd; /* The SSH command */
108
+ Blob sshBase; /* The base SSH command */
109
+ Blob aArgv[50]; /* The SSH arguments array */
110110
char *zHost; /* The host name to contact */
111111
int n; /* Size of prefix string */
112
+ int nToken, i;
112113
113114
socket_ssh_resolve_addr(pUrlData);
114
- zSsh = db_get("ssh-command", zDefaultSshCmd);
115
- blob_init(&zCmd, zSsh, -1);
115
+ blob_init(&sshBase, db_get("ssh-command", zDefaultSshCmd), -1);
116
+ blobarray_zero(aArgv, count(aArgv));
117
+ n = nToken = blob_tokenize(&sshBase, aArgv, count(aArgv));
116118
if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
117119
#ifdef _WIN32
118
- blob_appendf(&zCmd, " -P %d", pUrlData->port);
120
+ blob_append(&aArgv[nToken++], "-P", 2);
119121
#else
120
- blob_appendf(&zCmd, " -p %d", pUrlData->port);
122
+ blob_append(&aArgv[nToken++], "-p", 2);
121123
#endif
122
- }
123
- if( g.fSshTrace ){
124
- fossil_force_newline();
125
- fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
124
+ blob_appendf(&aArgv[nToken++], "%d", pUrlData->port);
126125
}
127126
if( pUrlData->user && pUrlData->user[0] ){
128127
zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
129128
}else{
130129
zHost = mprintf("%s", pUrlData->name);
131130
}
132
- n = blob_size(&zCmd);
133
- blob_append(&zCmd, " ", 1);
134
- shell_escape(&zCmd, stripLeadingMinus(zHost));
135
- blob_append(&zCmd, " ", 1);
136
- shell_escape(&zCmd, mprintf("%s", pUrlData->fossil));
137
- blob_append(&zCmd, " test-http", 10);
131
+ shell_escape(&aArgv[nToken++], stripLeadingMinus(zHost));
132
+ shell_escape(&aArgv[nToken++], mprintf("%s", pUrlData->fossil));
133
+ blob_append(&aArgv[nToken++], "test-http", 9);
138134
if( pUrlData->path && pUrlData->path[0] ){
139
- blob_append(&zCmd, " ", 1);
140
- shell_escape(&zCmd, mprintf("%s", stripLeadingMinus(pUrlData->path)));
135
+ shell_escape(&aArgv[nToken++],
136
+ mprintf("%s", stripLeadingMinus(pUrlData->path)));
141137
}
142138
if( g.fSshTrace ){
143
- fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
139
+ for(i=0; i<nToken; i++){
140
+ fossil_print("%s ", blob_str(&aArgv[i]));
141
+ }
142
+ fossil_force_newline();
144143
}
145144
free(zHost);
146
- popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
145
+ popen2(&aArgv[0], nToken, &sshIn, &sshOut, &sshPid);
147146
if( sshPid==0 ){
148
- socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
147
+ for(i=n; i<nToken; i++){
148
+ blob_appendf(&sshBase, " %b", &aArgv[i]);
149
+ }
150
+ socket_set_errmsg("cannot start ssh tunnel using [%b]", &sshBase);
149151
}
150
- blob_reset(&zCmd);
152
+ blobarray_reset(aArgv, nToken);
151153
return sshPid==0;
152154
}
153155
154156
/*
155157
** Open a connection to the server. The server is defined by the following
156158
--- src/http_transport.c
+++ src/http_transport.c
@@ -103,53 +103,55 @@
103 */
104 int transport_ssh_open(UrlData *pUrlData){
105 /* For SSH we need to create and run SSH fossil http
106 ** to talk to the remote machine.
107 */
108 char *zSsh; /* The base SSH command */
109 Blob zCmd; /* The SSH command */
110 char *zHost; /* The host name to contact */
111 int n; /* Size of prefix string */
 
112
113 socket_ssh_resolve_addr(pUrlData);
114 zSsh = db_get("ssh-command", zDefaultSshCmd);
115 blob_init(&zCmd, zSsh, -1);
 
116 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
117 #ifdef _WIN32
118 blob_appendf(&zCmd, " -P %d", pUrlData->port);
119 #else
120 blob_appendf(&zCmd, " -p %d", pUrlData->port);
121 #endif
122 }
123 if( g.fSshTrace ){
124 fossil_force_newline();
125 fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
126 }
127 if( pUrlData->user && pUrlData->user[0] ){
128 zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
129 }else{
130 zHost = mprintf("%s", pUrlData->name);
131 }
132 n = blob_size(&zCmd);
133 blob_append(&zCmd, " ", 1);
134 shell_escape(&zCmd, stripLeadingMinus(zHost));
135 blob_append(&zCmd, " ", 1);
136 shell_escape(&zCmd, mprintf("%s", pUrlData->fossil));
137 blob_append(&zCmd, " test-http", 10);
138 if( pUrlData->path && pUrlData->path[0] ){
139 blob_append(&zCmd, " ", 1);
140 shell_escape(&zCmd, mprintf("%s", stripLeadingMinus(pUrlData->path)));
141 }
142 if( g.fSshTrace ){
143 fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
 
 
 
144 }
145 free(zHost);
146 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
147 if( sshPid==0 ){
148 socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
 
 
 
149 }
150 blob_reset(&zCmd);
151 return sshPid==0;
152 }
153
154 /*
155 ** Open a connection to the server. The server is defined by the following
156
--- src/http_transport.c
+++ src/http_transport.c
@@ -103,53 +103,55 @@
103 */
104 int transport_ssh_open(UrlData *pUrlData){
105 /* For SSH we need to create and run SSH fossil http
106 ** to talk to the remote machine.
107 */
108 Blob sshBase; /* The base SSH command */
109 Blob aArgv[50]; /* The SSH arguments array */
110 char *zHost; /* The host name to contact */
111 int n; /* Size of prefix string */
112 int nToken, i;
113
114 socket_ssh_resolve_addr(pUrlData);
115 blob_init(&sshBase, db_get("ssh-command", zDefaultSshCmd), -1);
116 blobarray_zero(aArgv, count(aArgv));
117 n = nToken = blob_tokenize(&sshBase, aArgv, count(aArgv));
118 if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
119 #ifdef _WIN32
120 blob_append(&aArgv[nToken++], "-P", 2);
121 #else
122 blob_append(&aArgv[nToken++], "-p", 2);
123 #endif
124 blob_appendf(&aArgv[nToken++], "%d", pUrlData->port);
 
 
 
125 }
126 if( pUrlData->user && pUrlData->user[0] ){
127 zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
128 }else{
129 zHost = mprintf("%s", pUrlData->name);
130 }
131 shell_escape(&aArgv[nToken++], stripLeadingMinus(zHost));
132 shell_escape(&aArgv[nToken++], mprintf("%s", pUrlData->fossil));
133 blob_append(&aArgv[nToken++], "test-http", 9);
 
 
 
134 if( pUrlData->path && pUrlData->path[0] ){
135 shell_escape(&aArgv[nToken++],
136 mprintf("%s", stripLeadingMinus(pUrlData->path)));
137 }
138 if( g.fSshTrace ){
139 for(i=0; i<nToken; i++){
140 fossil_print("%s ", blob_str(&aArgv[i]));
141 }
142 fossil_force_newline();
143 }
144 free(zHost);
145 popen2(&aArgv[0], nToken, &sshIn, &sshOut, &sshPid);
146 if( sshPid==0 ){
147 for(i=n; i<nToken; i++){
148 blob_appendf(&sshBase, " %b", &aArgv[i]);
149 }
150 socket_set_errmsg("cannot start ssh tunnel using [%b]", &sshBase);
151 }
152 blobarray_reset(aArgv, nToken);
153 return sshPid==0;
154 }
155
156 /*
157 ** Open a connection to the server. The server is defined by the following
158
+18 -6
--- src/popen.c
+++ src/popen.c
@@ -111,26 +111,27 @@
111111
return rc!=0;
112112
}
113113
#endif
114114
115115
/*
116
-** Create a child process running shell command "zCmd". *ppOut is
116
+** Create a child process from array of arguments in "aArgv". *ppOut is
117117
** a FILE that becomes the standard input of the child process.
118118
** (The caller writes to *ppOut in order to send text to the child.)
119119
** *ppIn is stdout from the child process. (The caller
120120
** reads from *ppIn in order to receive input from the child.)
121121
** Note that *ppIn is an unbuffered file descriptor, not a FILE.
122122
** The process ID of the child is written into *pChildPid.
123123
**
124124
** Return the number of errors.
125125
*/
126
-int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){
126
+int popen2(Blob *aArgv, int nToken, int *pfdIn, FILE **ppOut, int *pChildPid){
127127
#ifdef _WIN32
128128
HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
129129
SECURITY_ATTRIBUTES saAttr;
130130
DWORD childPid = 0;
131
- int fd;
131
+ Blob shellcmd;
132
+ int fd, i;
132133
133134
saAttr.nLength = sizeof(saAttr);
134135
saAttr.bInheritHandle = TRUE;
135136
saAttr.lpSecurityDescriptor = NULL;
136137
hStderr = GetStdHandle(STD_ERROR_HANDLE);
@@ -142,11 +143,16 @@
142143
if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
143144
win32_fatal_error("cannot create pipe for stdin");
144145
}
145146
SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
146147
147
- win32_create_child_process(fossil_utf8_to_unicode(zCmd),
148
+ blob_zero(&shellcmd);
149
+ for(i=0; i<nToken-1; i++){
150
+ blob_appendf(&shellcmd, "%b ", aArgv[i]);
151
+ }
152
+ blob_trim(&shellcmd);
153
+ win32_create_child_process(fossil_utf8_to_unicode(blob_str(&shellcmd)),
148154
hStdinRd, hStdoutWr, hStderr,&childPid);
149155
*pChildPid = childPid;
150156
*pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
151157
fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
152158
*ppOut = _fdopen(fd, "w");
@@ -176,11 +182,12 @@
176182
*pChildPid = 0;
177183
return 1;
178184
}
179185
signal(SIGPIPE,SIG_IGN);
180186
if( *pChildPid==0 ){
181
- int fd;
187
+ char **pzArgs = 0;
188
+ int fd, i;
182189
int nErr = 0;
183190
/* This is the child process */
184191
close(0);
185192
fd = dup(pout[0]);
186193
if( fd!=0 ) nErr++;
@@ -189,11 +196,16 @@
189196
close(1);
190197
fd = dup(pin[1]);
191198
if( fd!=1 ) nErr++;
192199
close(pin[0]);
193200
close(pin[1]);
194
- execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
201
+ pzArgs = fossil_malloc( (nToken+1)*sizeof(pzArgs[0]) );
202
+ for(i=0; i<nToken; i++){
203
+ pzArgs[i] = blob_str(&aArgv[i]);
204
+ }
205
+ pzArgs[i] = 0;
206
+ execvp(pzArgs[0], pzArgs);
195207
return 1;
196208
}else{
197209
/* This is the parent process */
198210
close(pin[1]);
199211
*pfdIn = pin[0];
200212
--- src/popen.c
+++ src/popen.c
@@ -111,26 +111,27 @@
111 return rc!=0;
112 }
113 #endif
114
115 /*
116 ** Create a child process running shell command "zCmd". *ppOut is
117 ** a FILE that becomes the standard input of the child process.
118 ** (The caller writes to *ppOut in order to send text to the child.)
119 ** *ppIn is stdout from the child process. (The caller
120 ** reads from *ppIn in order to receive input from the child.)
121 ** Note that *ppIn is an unbuffered file descriptor, not a FILE.
122 ** The process ID of the child is written into *pChildPid.
123 **
124 ** Return the number of errors.
125 */
126 int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){
127 #ifdef _WIN32
128 HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
129 SECURITY_ATTRIBUTES saAttr;
130 DWORD childPid = 0;
131 int fd;
 
132
133 saAttr.nLength = sizeof(saAttr);
134 saAttr.bInheritHandle = TRUE;
135 saAttr.lpSecurityDescriptor = NULL;
136 hStderr = GetStdHandle(STD_ERROR_HANDLE);
@@ -142,11 +143,16 @@
142 if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
143 win32_fatal_error("cannot create pipe for stdin");
144 }
145 SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
146
147 win32_create_child_process(fossil_utf8_to_unicode(zCmd),
 
 
 
 
 
148 hStdinRd, hStdoutWr, hStderr,&childPid);
149 *pChildPid = childPid;
150 *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
151 fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
152 *ppOut = _fdopen(fd, "w");
@@ -176,11 +182,12 @@
176 *pChildPid = 0;
177 return 1;
178 }
179 signal(SIGPIPE,SIG_IGN);
180 if( *pChildPid==0 ){
181 int fd;
 
182 int nErr = 0;
183 /* This is the child process */
184 close(0);
185 fd = dup(pout[0]);
186 if( fd!=0 ) nErr++;
@@ -189,11 +196,16 @@
189 close(1);
190 fd = dup(pin[1]);
191 if( fd!=1 ) nErr++;
192 close(pin[0]);
193 close(pin[1]);
194 execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
 
 
 
 
 
195 return 1;
196 }else{
197 /* This is the parent process */
198 close(pin[1]);
199 *pfdIn = pin[0];
200
--- src/popen.c
+++ src/popen.c
@@ -111,26 +111,27 @@
111 return rc!=0;
112 }
113 #endif
114
115 /*
116 ** Create a child process from array of arguments in "aArgv". *ppOut is
117 ** a FILE that becomes the standard input of the child process.
118 ** (The caller writes to *ppOut in order to send text to the child.)
119 ** *ppIn is stdout from the child process. (The caller
120 ** reads from *ppIn in order to receive input from the child.)
121 ** Note that *ppIn is an unbuffered file descriptor, not a FILE.
122 ** The process ID of the child is written into *pChildPid.
123 **
124 ** Return the number of errors.
125 */
126 int popen2(Blob *aArgv, int nToken, int *pfdIn, FILE **ppOut, int *pChildPid){
127 #ifdef _WIN32
128 HANDLE hStdinRd, hStdinWr, hStdoutRd, hStdoutWr, hStderr;
129 SECURITY_ATTRIBUTES saAttr;
130 DWORD childPid = 0;
131 Blob shellcmd;
132 int fd, i;
133
134 saAttr.nLength = sizeof(saAttr);
135 saAttr.bInheritHandle = TRUE;
136 saAttr.lpSecurityDescriptor = NULL;
137 hStderr = GetStdHandle(STD_ERROR_HANDLE);
@@ -142,11 +143,16 @@
143 if( !CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 4096) ){
144 win32_fatal_error("cannot create pipe for stdin");
145 }
146 SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE);
147
148 blob_zero(&shellcmd);
149 for(i=0; i<nToken-1; i++){
150 blob_appendf(&shellcmd, "%b ", aArgv[i]);
151 }
152 blob_trim(&shellcmd);
153 win32_create_child_process(fossil_utf8_to_unicode(blob_str(&shellcmd)),
154 hStdinRd, hStdoutWr, hStderr,&childPid);
155 *pChildPid = childPid;
156 *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0);
157 fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0);
158 *ppOut = _fdopen(fd, "w");
@@ -176,11 +182,12 @@
182 *pChildPid = 0;
183 return 1;
184 }
185 signal(SIGPIPE,SIG_IGN);
186 if( *pChildPid==0 ){
187 char **pzArgs = 0;
188 int fd, i;
189 int nErr = 0;
190 /* This is the child process */
191 close(0);
192 fd = dup(pout[0]);
193 if( fd!=0 ) nErr++;
@@ -189,11 +196,16 @@
196 close(1);
197 fd = dup(pin[1]);
198 if( fd!=1 ) nErr++;
199 close(pin[0]);
200 close(pin[1]);
201 pzArgs = fossil_malloc( (nToken+1)*sizeof(pzArgs[0]) );
202 for(i=0; i<nToken; i++){
203 pzArgs[i] = blob_str(&aArgv[i]);
204 }
205 pzArgs[i] = 0;
206 execvp(pzArgs[0], pzArgs);
207 return 1;
208 }else{
209 /* This is the parent process */
210 close(pin[1]);
211 *pfdIn = pin[0];
212

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button