1 /*
2 sitecopy sftp/ssh protocol driver module (forked from rshdriver.c)
3 Copyright (C) 2000-2005, Joe Orton <joe@manyfish.co.uk>
4 Copyright (C) 2004, Nobuyuki Tsuchimura <tutimura@nn.iij4u.or.jp>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /* TODO:
22 * - handle ssh directly instead of handling sftp....(?)
23 */
24
25 #include <config.h>
26
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/socket.h>
30 #include <errno.h>
31
32 #ifdef HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35 #ifdef HAVE_STDARG_H
36 #include <stdarg.h>
37 #endif
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #include <ne_alloc.h>
46 #include <ne_string.h>
47 #include <ne_socket.h>
48
49 #include "protocol.h"
50 #include "lsparser.h"
51
52 #ifndef HAVE_SOCKETPAIR
53 #define USE_PIPES
54 #endif
55
56 typedef struct {
57 struct site *site;
58 const char *sftp_cmd, *ssh_cmd;
59 char buf[BUFSIZ];
60 int connected, fd_in, fd_out, sftp_pid;
61 FILE *fp; /* popen for sftp client */
62 } sftp_session;
63
64 static int run_sftp(sftp_session *sess, const char *template, ...)
65 ne_attribute((format (printf, 2, 3)));
66
read_sftp(sftp_session * sess)67 static int read_sftp(sftp_session *sess) {
68 size_t pos = 0;
69
70 do {
71 ssize_t ret = read(sess->fd_in, sess->buf+pos, BUFSIZ-pos);
72 if (ret < 0) return SITE_FAILED;
73 if (ret == 0) break;
74 pos += ret;
75 sess->buf[pos] = '\0';
76 } while (strchr(sess->buf, '>') == NULL && pos < BUFSIZ);
77 NE_DEBUG(DEBUG_SFTP, "(%s)", sess->buf);
78 return SITE_OK;
79 }
80
exec_sftp(sftp_session * sess)81 static void exec_sftp(sftp_session *sess)
82 {
83 size_t len;
84 char *username;
85
86 username = sess->site->server.username;
87 len = ne_snprintf(sess->buf, BUFSIZ, "%s%s%s",
88 (username != NULL ? username : ""),
89 (username != NULL ? "@" : ""),
90 sess->site->server.hostname);
91 if (len + 1 >= BUFSIZ) {
92 NE_DEBUG(DEBUG_SFTP, "sftp: user/host name too long.\n");
93 return;
94 }
95
96 execlp(sess->sftp_cmd, sess->sftp_cmd, sess->buf, NULL);
97 NE_DEBUG(DEBUG_SFTP, "sftp exec: %s %s: %s\n",
98 sess->sftp_cmd, sess->buf, strerror(errno));
99 }
100
sftp_connect(sftp_session * sess)101 static int sftp_connect(sftp_session *sess)
102 {
103 int c_in, c_out;
104 #ifdef USE_PIPES
105 int pin[2], pout[2];
106 if ((pipe(pin) == -1) || (pipe(pout) == -1)) {
107 NE_DEBUG(DEBUG_SFTP, "sftp pipe: %s", strerror(errno));
108 return SITE_FAILED;
109 }
110 sess->fd_in = pin[0];
111 sess->fd_out = pout[1];
112 c_in = pout[0];
113 c_out = pin[1];
114 #else /* !USE_PIPES */
115 int inout[2];
116 if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) {
117 NE_DEBUG(DEBUG_SFTP, "sftp socketpair: %s", strerror(errno));
118 return SITE_FAILED;
119 }
120 sess->fd_in = sess->fd_out = inout[0];
121 c_in = c_out = inout[1];
122 #endif /* USE_PIPES */
123
124 sess->connected = true;
125 sess->sftp_pid = fork();
126 switch (sess->sftp_pid) {
127 case -1:
128 NE_DEBUG(DEBUG_SFTP, "sftp: fork: %s", strerror(errno));
129 return SITE_FAILED;
130 case 0:
131 if ((dup2(c_in, STDIN_FILENO) == -1) ||
132 (dup2(c_out, STDOUT_FILENO) == -1)) {
133 NE_DEBUG(DEBUG_SFTP, "sftp: dup2: %s\n", strerror(errno));
134 return SITE_FAILED;
135 }
136 close(sess->fd_in); close(sess->fd_out);
137 close(c_in); close(c_out);
138 exec_sftp(sess);
139 return SITE_FAILED;
140 }
141 close(c_in);
142 close(c_out);
143 read_sftp(sess); /* wait for prompt */
144 return SITE_OK;
145 }
146
sftp_disconnect(sftp_session * sess)147 static int sftp_disconnect(sftp_session *sess)
148 {
149 #ifndef USE_PIPES
150 #ifndef SHUT_RDWR
151 #define SHUT_RDWR 2
152 #endif
153 shutdown(sess->fd_in, SHUT_RDWR);
154 shutdown(sess->fd_out, SHUT_RDWR);
155 #endif /* USE_PIPES */
156 close(sess->fd_in);
157 close(sess->fd_out);
158
159 if (waitpid(sess->sftp_pid, NULL, 0) == -1) {
160 NE_DEBUG(DEBUG_SFTP, "sftp: Couldn't wait for sftp process: %s",
161 strerror(errno));
162 return SITE_FAILED;
163 }
164 return SITE_OK;
165 }
166
167
run_sftp(sftp_session * sess,const char * template,...)168 static int run_sftp(sftp_session *sess, const char *template, ...)
169 {
170 va_list params;
171 if (!sess->connected) sftp_connect(sess);
172
173 va_start(params, template);
174 ne_vsnprintf(sess->buf, BUFSIZ, template, params);
175 va_end(params);
176
177 if (strlen(sess->buf) + 2 >= BUFSIZ) { /* will be added '\n' */
178 NE_DEBUG(DEBUG_SFTP, "sftp: sftp: too long command '%s'.\n", sess->buf);
179 return SITE_FAILED; /* buff over flow */
180 }
181 NE_DEBUG(DEBUG_SFTP, "SFTP: '%s'\n", sess->buf);
182
183 strcat(sess->buf, "\n");
184 if (write(sess->fd_out, sess->buf, strlen(sess->buf)) == -1) {
185 return SITE_FAILED;
186 }
187 read_sftp(sess); /* wait for prompt */
188 return SITE_OK;
189 }
190
191
192 static int run_ssh(sftp_session *sess, const char *template, ...)
193 ne_attribute((format (printf, 2, 3)));
194
run_ssh(sftp_session * sess,const char * template,...)195 static int run_ssh(sftp_session *sess, const char *template, ...)
196 {
197 va_list params;
198 char *buf = sess->buf;
199 size_t len;
200 char *username = sess->site->server.username;
201
202 len = ne_snprintf(buf, BUFSIZ, "%s%s%s %s \\\\",
203 sess->ssh_cmd,
204 (username != NULL ? " -l " : ""),
205 (username != NULL ? username : ""),
206 sess->site->server.hostname);
207
208 va_start(params, template);
209 len += ne_vsnprintf(buf + len, BUFSIZ - len, template, params);
210 va_end(params);
211
212 len += ne_snprintf(buf + len, BUFSIZ - len, " 2> /dev/null" );
213 if (len + 1 >= BUFSIZ) return SITE_FAILED; /* buff over flow */
214 NE_DEBUG(DEBUG_SFTP, "rcmd: %s\n", buf);
215
216 sess->fp = popen(buf, "r");
217 if (sess->fp != NULL) {
218 return SITE_OK;
219 } else {
220 return SITE_FAILED;
221 }
222 /* why not free(cmd)? */
223 }
224
ssh_finish(sftp_session * sess)225 static int ssh_finish(sftp_session *sess)
226 {
227 if (pclose(sess->fp) == 0) {
228 sess->fp = NULL;
229 return SITE_OK;
230 } else {
231 return SITE_FAILED;
232 }
233 }
234
init(void ** session,struct site * site)235 static int init(void **session, struct site *site)
236 {
237 sftp_session *sess = ne_calloc(sizeof *sess);
238 *session = sess;
239 if (site->rcp_cmd != NULL) {
240 sess->sftp_cmd = site->rcp_cmd;
241 } else {
242 sess->sftp_cmd = "sftp";
243 }
244 if (site->rsh_cmd != NULL) {
245 sess->ssh_cmd = site->rsh_cmd;
246 } else {
247 sess->ssh_cmd = "ssh";
248 }
249 sess->site = site;
250 sess->connected = false;
251 return SITE_OK;
252 }
253
finish(void * session)254 static void finish(void *session)
255 {
256 sftp_session *sess = session;
257
258 if (sess->connected) sftp_disconnect(sess);
259 if (sess->fp != NULL) ssh_finish(sess);
260 free(sess);
261 }
262
file_move(void * session,const char * from,const char * to)263 static int file_move(void *session, const char *from, const char *to)
264 {
265 sftp_session *sess = session;
266 return run_sftp(sess, "rename %s %s", from, to);
267 }
268
file_upload(void * session,const char * local,const char * remote,int ascii)269 static int file_upload(void *session, const char *local, const char *remote,
270 int ascii)
271 {
272 sftp_session *sess = session;
273 return run_sftp(sess, "put %s %s", local, remote);
274 }
275
file_upload_cond(void * session,const char * local,const char * remote,int ascii,time_t t)276 static int file_upload_cond(void *session,
277 const char *local, const char *remote,
278 int ascii, time_t t)
279 {
280 return SITE_UNSUPPORTED;
281 }
282
file_get_modtime(void * sess,const char * remote,time_t * modtime)283 static int file_get_modtime(void *sess, const char *remote, time_t *modtime)
284 {
285 return SITE_UNSUPPORTED;
286 }
287
file_download(void * session,const char * local,const char * remote,int ascii)288 static int file_download(void *session, const char *local, const char *remote,
289 int ascii)
290 {
291 sftp_session *sess = session;
292 return run_sftp(sess, "get %s %s", remote, local);
293 }
294
295
file_read(void * session,const char * remote,ne_block_reader reader,void * userdata)296 static int file_read(void *session, const char *remote,
297 ne_block_reader reader, void *userdata)
298 {
299 sftp_session *sess = session;
300 size_t len;
301
302 if (run_ssh(sess, "cat '%s'", remote) != SITE_OK)
303 return SITE_FAILED;
304
305 while ((len = fread(sess->buf, 1, BUFSIZ, sess->fp)) > 0) {
306 reader(userdata, sess->buf, len);
307 }
308
309 return ssh_finish(sess);
310 }
311
file_delete(void * session,const char * filename)312 static int file_delete(void *session, const char *filename)
313 {
314 sftp_session *sess = session;
315 return run_sftp(sess, "rm %s", filename);
316 }
317
file_chmod(void * session,const char * fname,const mode_t mode)318 static int file_chmod(void *session, const char *fname, const mode_t mode)
319 {
320 sftp_session *sess = session;
321 return run_sftp(sess, "chmod %03o %s", mode, fname);
322 }
323
dir_create(void * session,const char * dirname)324 static int dir_create(void *session, const char *dirname)
325 {
326 sftp_session *sess = session;
327 return run_sftp(sess, "mkdir %s", dirname);
328 }
329
dir_remove(void * session,const char * dirname)330 static int dir_remove(void *session, const char *dirname)
331 {
332 sftp_session *sess = session;
333 return run_sftp(sess, "rmdir %s", dirname);
334 }
335
error(void * session)336 static const char *error(void *session)
337 {
338 return "An error occurred.";
339 }
340
get_dummy_port(struct site * site)341 static int get_dummy_port(struct site *site)
342 {
343 return 0;
344 }
345
ssh_fetch(sftp_session * sess,const char * startdir,struct proto_file ** list)346 static int ssh_fetch(sftp_session *sess, const char *startdir,
347 struct proto_file **list)
348 {
349 struct proto_file *tail = NULL;
350 int success = SITE_OK;
351 ls_context_t *lsctx = ls_init(startdir);
352
353 memset(sess->buf, 0, BUFSIZ);
354
355 while (fgets(sess->buf, BUFSIZ, sess->fp) != NULL) {
356 enum ls_result res;
357 struct ls_file lfile;
358
359 res = ls_parse(lsctx, sess->buf, &lfile);
360
361 switch (res) {
362 case ls_error:
363 NE_DEBUG(DEBUG_SFTP, "Could not parse line.\n");
364 success = SITE_ERRORS;
365 break;
366
367 default:
368 ls_pflist_add(list, &tail, &lfile, res);
369 break;
370 }
371 }
372
373 ls_destroy(lsctx);
374
375 NE_DEBUG(DEBUG_SFTP, "Fetch finished successfully.\n");
376 return success;
377 }
378
fetch_list(void * session,const char * dirname,int need_modtimes,struct proto_file ** files)379 static int fetch_list(void *session, const char *dirname, int need_modtimes,
380 struct proto_file **files)
381 {
382 sftp_session *sess = session;
383 int ret;
384
385 ret = run_ssh(sess, "ls -la '%s'", dirname);
386 if (ret == SITE_OK) {
387 ssh_fetch(sess, dirname, files);
388 ret = ssh_finish(sess);
389 }
390
391 /*
392 if (ret == SITE_OK && need_modtimes) {
393 ret = sftp_fetch_modtimes(sess, dirname, *files);
394 }
395 */
396
397 return ret;
398 }
399
400 /* The protocol driver */
401 const struct proto_driver sftp_driver = {
402 init,
403 finish,
404 file_move,
405 file_upload,
406 file_upload_cond,
407 file_get_modtime,
408 file_download,
409 file_read,
410 file_delete,
411 file_chmod,
412 dir_create,
413 dir_remove,
414 NULL, /* create link */
415 NULL, /* change link target */
416 NULL, /* delete link */
417 fetch_list, /* fetch list. */
418 error,
419 get_dummy_port,
420 get_dummy_port,
421 "sftp/ssh"
422 };
423