Coverage Report

Created: 2026-07-05 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/scp.c
Line
Count
Source
1
/*
2
 * scp - SSH scp wrapper functions
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2009 by Aris Adamantiadis <aris@0xbadc0de.be>
7
 *
8
 * The SSH Library is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published by
10
 * the Free Software Foundation; either version 2.1 of the License, or (at your
11
 * option) any later version.
12
 *
13
 * The SSH Library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16
 * License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with the SSH Library; see the file COPYING.  If not, write to
20
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21
 * MA 02111-1307, USA.
22
 */
23
24
#include "config.h"
25
26
#include <stdio.h>
27
#include <string.h>
28
#include <stdlib.h>
29
30
#include "libssh/priv.h"
31
#include "libssh/scp.h"
32
#include "libssh/misc.h"
33
#include "libssh/session.h"
34
35
/**
36
 * @defgroup libssh_scp The SSH scp functions
37
 * @ingroup libssh
38
 *
39
 * SCP protocol over SSH functions
40
 *
41
 * @deprecated Please use SFTP instead
42
 *
43
 * @{
44
 */
45
46
/**
47
 * @deprecated Please use SFTP instead
48
 *
49
 * @brief Create a new scp session.
50
 *
51
 * @param[in]  session  The SSH session to use.
52
 *
53
 * @param[in]  mode     One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you
54
 *                      need to drop files remotely or read them.
55
 *                      It is not possible to combine read and write.
56
 *                      SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate
57
 *                      that you're going to use recursion. Browsing through
58
 *                      directories is not possible without this.
59
 *
60
 * @param[in]  location The directory in which write or read will be done. Any
61
 *                      push or pull will be relative to this place.
62
 *                      This can also be a pattern of files to download (read).
63
 *
64
 * @returns             A ssh_scp handle, NULL if the creation was impossible.
65
 */
66
ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location)
67
273
{
68
273
    ssh_scp scp = NULL;
69
70
273
    if (session == NULL || location == NULL) {
71
0
        goto error;
72
0
    }
73
74
273
    scp = (ssh_scp)calloc(1, sizeof(struct ssh_scp_struct));
75
273
    if (scp == NULL) {
76
0
        ssh_set_error(session, SSH_FATAL,
77
0
                      "Error allocating memory for ssh_scp");
78
0
        goto error;
79
0
    }
80
81
273
    if ((mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE &&
82
273
        (mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_READ)
83
0
    {
84
0
        ssh_set_error(session, SSH_FATAL,
85
0
                      "Invalid mode %d for ssh_scp_new()", mode);
86
0
        goto error;
87
0
    }
88
89
273
    if (strlen(location) > 32 * 1024) {
90
0
        ssh_set_error(session, SSH_FATAL,
91
0
                      "Location path is too long");
92
0
        goto error;
93
0
    }
94
95
273
    scp->location = strdup(location);
96
273
    if (scp->location == NULL) {
97
0
        ssh_set_error(session, SSH_FATAL,
98
0
                      "Error allocating memory for ssh_scp");
99
0
        goto error;
100
0
    }
101
102
273
    scp->session = session;
103
273
    scp->mode = mode & ~SSH_SCP_RECURSIVE;
104
273
    scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0;
105
273
    scp->channel = NULL;
106
273
    scp->state = SSH_SCP_NEW;
107
108
273
    return scp;
109
110
0
error:
111
0
    ssh_scp_free(scp);
112
0
    return NULL;
113
273
}
114
115
/**
116
 * @deprecated Please use SFTP instead
117
 *
118
 * @brief Initialize the scp channel.
119
 *
120
 * @param[in]  scp      The scp context to initialize.
121
 *
122
 * @return SSH_OK on success or an SSH error code.
123
 *
124
 * @see ssh_scp_new()
125
 */
126
int ssh_scp_init(ssh_scp scp)
127
273
{
128
273
    int rc;
129
273
    char execbuffer[PATH_MAX] = {0};
130
273
    char *quoted_location = NULL;
131
273
    size_t quoted_location_len = 0;
132
273
    size_t scp_location_len;
133
134
273
    if (scp == NULL) {
135
0
        return SSH_ERROR;
136
0
    }
137
138
273
    if (scp->state != SSH_SCP_NEW) {
139
0
        ssh_set_error(scp->session, SSH_FATAL,
140
0
                      "ssh_scp_init called under invalid state");
141
0
        return SSH_ERROR;
142
0
    }
143
144
273
    if (scp->location == NULL) {
145
0
        ssh_set_error(scp->session, SSH_FATAL,
146
0
                      "Invalid scp context: location is NULL");
147
0
        return SSH_ERROR;
148
0
    }
149
150
273
    SSH_LOG(SSH_LOG_DEBUG, "Initializing scp session %s %son location '%s'",
151
273
            scp->mode == SSH_SCP_WRITE?"write":"read",
152
273
            scp->recursive ? "recursive " : "",
153
273
            scp->location);
154
155
273
    scp->channel = ssh_channel_new(scp->session);
156
273
    if (scp->channel == NULL) {
157
0
        ssh_set_error(scp->session, SSH_FATAL,
158
0
                      "Channel creation failed for scp");
159
0
        scp->state = SSH_SCP_ERROR;
160
0
        return SSH_ERROR;
161
0
    }
162
163
273
    rc = ssh_channel_open_session(scp->channel);
164
273
    if (rc == SSH_ERROR) {
165
0
        ssh_set_error(scp->session, SSH_FATAL,
166
0
                      "Failed to open channel for scp");
167
0
        scp->state = SSH_SCP_ERROR;
168
0
        return SSH_ERROR;
169
0
    }
170
171
    /* In the worst case, each character would be replaced by 3 plus the string
172
     * terminator '\0' */
173
273
    scp_location_len = strlen(scp->location);
174
273
    quoted_location_len = ((size_t)3 * scp_location_len) + 1;
175
    /* Paranoia check */
176
273
    if (quoted_location_len < scp_location_len) {
177
0
        ssh_set_error(scp->session, SSH_FATAL,
178
0
                      "Buffer overflow detected");
179
0
        scp->state = SSH_SCP_ERROR;
180
0
        return SSH_ERROR;
181
0
    }
182
183
273
    quoted_location = (char *)calloc(1, quoted_location_len);
184
273
    if (quoted_location == NULL) {
185
0
        ssh_set_error(scp->session, SSH_FATAL,
186
0
                      "Failed to allocate memory for quoted location");
187
0
        scp->state = SSH_SCP_ERROR;
188
0
        return SSH_ERROR;
189
0
    }
190
191
273
    rc = ssh_quote_file_name(scp->location, quoted_location,
192
273
                             quoted_location_len);
193
273
    if (rc <= 0) {
194
0
        ssh_set_error(scp->session, SSH_FATAL,
195
0
                      "Failed to single quote command location");
196
0
        SAFE_FREE(quoted_location);
197
0
        scp->state = SSH_SCP_ERROR;
198
0
        return SSH_ERROR;
199
0
    }
200
201
    /* Some servers do not handle the quoting well. Pass in the raw file
202
     * location */
203
273
    if (scp->session->flags & SSH_SESSION_FLAG_SCP_QUOTING_BROKEN) {
204
0
        free(quoted_location);
205
0
        quoted_location = strdup(scp->location);
206
0
        if (quoted_location == NULL) {
207
0
            ssh_set_error_oom(scp->session);
208
0
            return SSH_ERROR;
209
0
        }
210
0
    }
211
212
273
    if (scp->mode == SSH_SCP_WRITE) {
213
0
        snprintf(execbuffer, sizeof(execbuffer), "scp -t %s %s",
214
0
                scp->recursive ? "-r" : "", quoted_location);
215
273
    } else {
216
273
        snprintf(execbuffer, sizeof(execbuffer), "scp -f %s %s",
217
273
                scp->recursive ? "-r" : "", quoted_location);
218
273
    }
219
220
273
    SAFE_FREE(quoted_location);
221
222
273
    SSH_LOG(SSH_LOG_DEBUG, "Executing command: %s", execbuffer);
223
224
273
    rc = ssh_channel_request_exec(scp->channel, execbuffer);
225
273
    if (rc == SSH_ERROR){
226
273
        ssh_set_error(scp->session, SSH_FATAL,
227
273
                      "Failed executing command: %s", execbuffer);
228
273
        scp->state = SSH_SCP_ERROR;
229
273
        return SSH_ERROR;
230
273
    }
231
232
0
    if (scp->mode == SSH_SCP_WRITE) {
233
0
        rc = ssh_scp_response(scp, NULL);
234
0
        if (rc != 0) {
235
0
            return SSH_ERROR;
236
0
        }
237
0
    } else {
238
0
        ssh_channel_write(scp->channel, "", 1);
239
0
    }
240
241
0
    if (scp->mode == SSH_SCP_WRITE) {
242
0
        scp->state = SSH_SCP_WRITE_INITED;
243
0
    } else {
244
0
        scp->state = SSH_SCP_READ_INITED;
245
0
    }
246
247
0
    return SSH_OK;
248
0
}
249
250
/**
251
 * @deprecated Please use SFTP instead
252
 *
253
 * @brief Close the scp channel.
254
 *
255
 * @param[in]  scp      The scp context to close.
256
 *
257
 * @return SSH_OK on success or an SSH error code.
258
 *
259
 * @see ssh_scp_init()
260
 */
261
int ssh_scp_close(ssh_scp scp)
262
273
{
263
273
    char buffer[128] = {0};
264
273
    int rc;
265
266
273
    if (scp == NULL) {
267
0
        return SSH_ERROR;
268
0
    }
269
270
273
    if (scp->channel != NULL) {
271
273
        if (ssh_channel_send_eof(scp->channel) == SSH_ERROR) {
272
0
            scp->state = SSH_SCP_ERROR;
273
0
            return SSH_ERROR;
274
0
        }
275
        /* avoid situations where data are buffered and
276
         * not yet stored on disk. This can happen if the close is sent
277
         * before we got the EOF back
278
         */
279
273
        while (!ssh_channel_is_eof(scp->channel)) {
280
273
            rc = ssh_channel_read(scp->channel, buffer, sizeof(buffer), 0);
281
273
            if (rc == SSH_ERROR || rc == SSH_AGAIN || rc == 0) {
282
273
                break;
283
273
            }
284
273
        }
285
286
273
        if (ssh_channel_close(scp->channel) == SSH_ERROR) {
287
0
            scp->state = SSH_SCP_ERROR;
288
0
            return SSH_ERROR;
289
0
        }
290
291
273
        ssh_channel_free(scp->channel);
292
273
        scp->channel = NULL;
293
273
    }
294
295
273
    scp->state = SSH_SCP_NEW;
296
273
    return SSH_OK;
297
273
}
298
299
/**
300
 * @deprecated Please use SFTP instead
301
 *
302
 * @brief Free a scp context.
303
 *
304
 * @param[in]  scp      The context to free.
305
 *
306
 * @see ssh_scp_new()
307
 */
308
void ssh_scp_free(ssh_scp scp)
309
273
{
310
273
    if (scp == NULL) {
311
0
        return;
312
0
    }
313
314
273
    if (scp->state != SSH_SCP_NEW) {
315
0
        ssh_scp_close(scp);
316
0
    }
317
318
273
    if (scp->channel) {
319
0
        ssh_channel_free(scp->channel);
320
0
    }
321
322
273
    SAFE_FREE(scp->location);
323
273
    SAFE_FREE(scp->request_name);
324
273
    SAFE_FREE(scp->warning);
325
273
    SAFE_FREE(scp);
326
273
}
327
328
/**
329
 * @deprecated Please use SFTP instead
330
 *
331
 * @brief Create a directory in a scp in sink mode.
332
 *
333
 * @param[in]  scp      The scp handle.
334
 *
335
 * @param[in]  dirname  The name of the directory being created.
336
 *
337
 * @param[in]  mode     The UNIX permissions for the new directory, e.g. 0755.
338
 *
339
 * @returns             SSH_OK if the directory has been created, SSH_ERROR if
340
 *                      an error occurred.
341
 *
342
 * @see ssh_scp_leave_directory()
343
 */
344
int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
345
0
{
346
0
    char buffer[PATH_MAX] = {0};
347
0
    int rc;
348
0
    char *dir = NULL;
349
0
    char *perms = NULL;
350
0
    char *vis_encoded = NULL;
351
0
    size_t vis_encoded_len;
352
353
0
    if (scp == NULL) {
354
0
        return SSH_ERROR;
355
0
    }
356
357
0
    if (scp->state != SSH_SCP_WRITE_INITED) {
358
0
        ssh_set_error(scp->session, SSH_FATAL,
359
0
                      "ssh_scp_push_directory called under invalid state");
360
0
        return SSH_ERROR;
361
0
    }
362
363
0
    dir = ssh_basename(dirname);
364
0
    if (dir == NULL) {
365
0
        ssh_set_error_oom(scp->session);
366
0
        return SSH_ERROR;
367
0
    }
368
369
0
    vis_encoded_len = (2 * strlen(dir)) + 1;
370
0
    vis_encoded = (char *)calloc(1, vis_encoded_len);
371
0
    if (vis_encoded == NULL) {
372
0
        ssh_set_error(scp->session, SSH_FATAL,
373
0
                      "Failed to allocate buffer to vis encode directory name");
374
0
        goto error;
375
0
    }
376
377
0
    rc = ssh_newline_vis(dir, vis_encoded, vis_encoded_len);
378
0
    if (rc <= 0) {
379
0
        ssh_set_error(scp->session, SSH_FATAL,
380
0
                      "Failed to vis encode directory name");
381
0
        goto error;
382
0
    }
383
384
0
    perms = ssh_scp_string_mode(mode);
385
0
    if (perms == NULL) {
386
0
        ssh_set_error(scp->session, SSH_FATAL,
387
0
                      "Failed to get directory permission string");
388
0
        goto error;
389
0
    }
390
391
0
    SSH_LOG(SSH_LOG_DEBUG,
392
0
            "SCP pushing directory %s with permissions '%s'",
393
0
            vis_encoded, perms);
394
395
    /* Use vis encoded directory name */
396
0
    snprintf(buffer, sizeof(buffer),
397
0
             "D%s 0 %s\n",
398
0
             perms, vis_encoded);
399
400
0
    SAFE_FREE(dir);
401
0
    SAFE_FREE(perms);
402
0
    SAFE_FREE(vis_encoded);
403
404
0
    rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
405
0
    if (rc == SSH_ERROR) {
406
0
        scp->state = SSH_SCP_ERROR;
407
0
        return SSH_ERROR;
408
0
    }
409
410
0
    rc = ssh_scp_response(scp, NULL);
411
0
    if (rc != 0) {
412
0
        return SSH_ERROR;
413
0
    }
414
415
0
    return SSH_OK;
416
417
0
error:
418
0
    SAFE_FREE(dir);
419
0
    SAFE_FREE(perms);
420
0
    SAFE_FREE(vis_encoded);
421
422
0
    return SSH_ERROR;
423
0
}
424
425
/**
426
 * @deprecated Please use SFTP instead
427
 *
428
 * @brief Leave a directory.
429
 *
430
 * @returns             SSH_OK if the directory has been left, SSH_ERROR if an
431
 *                      error occurred.
432
 *
433
 * @see ssh_scp_push_directory()
434
 */
435
int ssh_scp_leave_directory(ssh_scp scp)
436
0
{
437
0
    char buffer[] = "E\n";
438
0
    int rc;
439
440
0
    if (scp == NULL) {
441
0
        return SSH_ERROR;
442
0
    }
443
444
0
    if (scp->state != SSH_SCP_WRITE_INITED) {
445
0
        ssh_set_error(scp->session, SSH_FATAL,
446
0
                      "ssh_scp_leave_directory called under invalid state");
447
0
        return SSH_ERROR;
448
0
    }
449
450
0
    rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
451
0
    if (rc == SSH_ERROR) {
452
0
        scp->state = SSH_SCP_ERROR;
453
0
        return SSH_ERROR;
454
0
    }
455
456
0
    rc = ssh_scp_response(scp, NULL);
457
0
    if (rc != 0) {
458
0
        return SSH_ERROR;
459
0
    }
460
461
0
    return SSH_OK;
462
0
}
463
464
/**
465
 * @deprecated Please use SFTP instead
466
 *
467
 * @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit
468
 * size.
469
 *
470
 * @param[in]  scp      The scp handle.
471
 *
472
 * @param[in]  filename The name of the file being sent. It should not contain
473
 *                      any path indicator
474
 *
475
 * @param[in]  size     Exact size in bytes of the file being sent.
476
 *
477
 * @param[in]  mode     The UNIX permissions for the new file, e.g. 0644.
478
 *
479
 * @returns             SSH_OK if the file is ready to be sent, SSH_ERROR if an
480
 *                      error occurred.
481
 *
482
 * @see ssh_scp_push_file()
483
 */
484
int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
485
                        int mode)
486
0
{
487
0
    char buffer[PATH_MAX] = {0};
488
0
    int rc;
489
0
    char *file = NULL;
490
0
    char *perms = NULL;
491
0
    char *vis_encoded = NULL;
492
0
    size_t vis_encoded_len;
493
494
0
    if (scp == NULL) {
495
0
        return SSH_ERROR;
496
0
    }
497
498
0
    if (scp->state != SSH_SCP_WRITE_INITED) {
499
0
        ssh_set_error(scp->session, SSH_FATAL,
500
0
                      "ssh_scp_push_file called under invalid state");
501
0
        return SSH_ERROR;
502
0
    }
503
504
0
    file = ssh_basename(filename);
505
0
    if (file == NULL) {
506
0
        ssh_set_error_oom(scp->session);
507
0
        return SSH_ERROR;
508
0
    }
509
510
0
    vis_encoded_len = (2 * strlen(file)) + 1;
511
0
    vis_encoded = (char *)calloc(1, vis_encoded_len);
512
0
    if (vis_encoded == NULL) {
513
0
        ssh_set_error(scp->session, SSH_FATAL,
514
0
                      "Failed to allocate buffer to vis encode file name");
515
0
        goto error;
516
0
    }
517
518
0
    rc = ssh_newline_vis(file, vis_encoded, vis_encoded_len);
519
0
    if (rc <= 0) {
520
0
        ssh_set_error(scp->session, SSH_FATAL,
521
0
                      "Failed to vis encode file name");
522
0
        goto error;
523
0
    }
524
525
0
    perms = ssh_scp_string_mode(mode);
526
0
    if (perms == NULL) {
527
0
        ssh_set_error(scp->session, SSH_FATAL,
528
0
                      "Failed to get file permission string");
529
0
        goto error;
530
0
    }
531
532
0
    SSH_LOG(SSH_LOG_DEBUG,
533
0
            "SCP pushing file %s, size %" PRIu64 " with permissions '%s'",
534
0
            vis_encoded, size, perms);
535
536
    /* Use vis encoded file name */
537
0
    snprintf(buffer, sizeof(buffer),
538
0
             "C%s %" PRIu64 " %s\n",
539
0
             perms, size, vis_encoded);
540
541
0
    SAFE_FREE(file);
542
0
    SAFE_FREE(perms);
543
0
    SAFE_FREE(vis_encoded);
544
545
0
    rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
546
0
    if (rc == SSH_ERROR) {
547
0
        scp->state = SSH_SCP_ERROR;
548
0
        return SSH_ERROR;
549
0
    }
550
551
0
    rc = ssh_scp_response(scp, NULL);
552
0
    if (rc != 0) {
553
0
        return SSH_ERROR;
554
0
    }
555
556
0
    scp->filelen = size;
557
0
    scp->processed = 0;
558
0
    scp->state = SSH_SCP_WRITE_WRITING;
559
560
0
    return SSH_OK;
561
562
0
error:
563
0
    SAFE_FREE(file);
564
0
    SAFE_FREE(perms);
565
0
    SAFE_FREE(vis_encoded);
566
567
0
    return SSH_ERROR;
568
0
}
569
570
/**
571
 * @deprecated Please use SFTP instead
572
 *
573
 * @brief Initialize the sending of a file to a scp in sink mode.
574
 *
575
 * @param[in]  scp      The scp handle.
576
 *
577
 * @param[in]  filename The name of the file being sent. It should not contain
578
 *                      any path indicator
579
 *
580
 * @param[in]  size     Exact size in bytes of the file being sent.
581
 *
582
 * @param[in]  mode     The UNIX permissions for the new file, e.g. 0644.
583
 *
584
 * @returns             SSH_OK if the file is ready to be sent, SSH_ERROR if an
585
 *                      error occurred.
586
 */
587
int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
588
0
{
589
0
    return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode);
590
0
}
591
592
/**
593
 * @internal
594
 *
595
 * @deprecated Please use SFTP instead
596
 *
597
 * @brief Wait for a response of the scp server.
598
 *
599
 * @param[in]  scp      The scp handle.
600
 *
601
 * @param[out] response A pointer where the response message must be copied if
602
 *                      any. This pointer must then be free'd.
603
 *
604
 * @returns             The return code, SSH_ERROR a error occurred.
605
 */
606
int ssh_scp_response(ssh_scp scp, char **response)
607
0
{
608
0
    unsigned char code;
609
0
    int rc;
610
0
    char msg[128] = {0};
611
612
0
    if (scp == NULL) {
613
0
        return SSH_ERROR;
614
0
    }
615
616
0
    rc = ssh_channel_read(scp->channel, &code, 1, 0);
617
0
    if (rc == SSH_ERROR) {
618
0
        scp->state = SSH_SCP_ERROR;
619
0
        return SSH_ERROR;
620
0
    }
621
0
    if (rc == SSH_AGAIN) {
622
0
        ssh_set_error(scp->session, SSH_FATAL, "SCP: ssh_channel_read timeout");
623
0
        scp->state = SSH_SCP_ERROR;
624
0
        return SSH_ERROR;
625
0
    }
626
627
0
    if (code == 0) {
628
0
        return 0;
629
0
    }
630
631
0
    if (code > 2) {
632
0
        ssh_set_error(scp->session, SSH_FATAL,
633
0
                      "SCP: invalid status code %u received", code);
634
0
        scp->state = SSH_SCP_ERROR;
635
0
        return SSH_ERROR;
636
0
    }
637
638
0
    rc = ssh_scp_read_string(scp, msg, sizeof(msg));
639
0
    if (rc == SSH_ERROR) {
640
0
        return rc;
641
0
    }
642
643
    /* Warning */
644
0
    if (code == 1) {
645
0
        ssh_set_error(scp->session, SSH_REQUEST_DENIED,
646
0
                      "SCP: Warning: status code 1 received: %s", msg);
647
0
        SSH_LOG(SSH_LOG_RARE,
648
0
                "SCP: Warning: status code 1 received: %s", msg);
649
0
        if (response) {
650
0
            *response = strdup(msg);
651
0
        }
652
0
        return 1;
653
0
    }
654
655
0
    if (code == 2) {
656
0
        ssh_set_error(scp->session, SSH_FATAL,
657
0
                      "SCP: Error: status code 2 received: %s", msg);
658
0
        if (response) {
659
0
            *response = strdup(msg);
660
0
        }
661
0
        return 2;
662
0
    }
663
664
    /* Not reached */
665
0
    return SSH_ERROR;
666
0
}
667
668
/**
669
 * @deprecated Please use SFTP instead
670
 *
671
 * @brief Write into a remote scp file.
672
 *
673
 * @param[in]  scp      The scp handle.
674
 *
675
 * @param[in]  buffer   The buffer to write.
676
 *
677
 * @param[in]  len      The number of bytes to write.
678
 *
679
 * @returns             SSH_OK if the write was successful, SSH_ERROR an error
680
 *                      occurred while writing.
681
 */
682
int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
683
0
{
684
0
    int w;
685
0
    int rc;
686
0
    uint8_t code;
687
688
0
    if (scp == NULL) {
689
0
        return SSH_ERROR;
690
0
    }
691
692
0
    if (scp->state != SSH_SCP_WRITE_WRITING) {
693
0
        ssh_set_error(scp->session, SSH_FATAL,
694
0
                      "ssh_scp_write called under invalid state");
695
0
        return SSH_ERROR;
696
0
    }
697
698
0
    if (scp->processed + len > scp->filelen) {
699
0
        len = (size_t) (scp->filelen - scp->processed);
700
0
    }
701
702
    /* hack to avoid waiting for window change */
703
0
    rc = ssh_channel_poll(scp->channel, 0);
704
0
    if (rc == SSH_ERROR) {
705
0
        scp->state = SSH_SCP_ERROR;
706
0
        return SSH_ERROR;
707
0
    }
708
709
0
    w = ssh_channel_write(scp->channel, buffer, len);
710
0
    if (w != SSH_ERROR) {
711
0
        scp->processed += w;
712
0
    } else {
713
0
        scp->state = SSH_SCP_ERROR;
714
0
        return SSH_ERROR;
715
0
    }
716
717
    /* Far end sometimes send a status message, which we need to read
718
     * and handle */
719
0
    rc = ssh_channel_poll(scp->channel, 0);
720
0
    if (rc > 0) {
721
0
        rc = ssh_scp_response(scp, NULL);
722
0
        if (rc != 0) {
723
0
            return SSH_ERROR;
724
0
        }
725
0
    }
726
727
    /* Check if we arrived at end of file */
728
0
    if (scp->processed == scp->filelen) {
729
0
        code = 0;
730
0
        w = ssh_channel_write(scp->channel, &code, 1);
731
0
        if (w == SSH_ERROR) {
732
0
            scp->state = SSH_SCP_ERROR;
733
0
            return SSH_ERROR;
734
0
        }
735
736
0
        scp->processed = scp->filelen = 0;
737
0
        scp->state = SSH_SCP_WRITE_INITED;
738
0
    }
739
740
0
    return SSH_OK;
741
0
}
742
743
/**
744
 * @deprecated Please use SFTP instead
745
 *
746
 * @brief Read a string on a channel, terminated by '\n'
747
 *
748
 * @param[in]  scp      The scp handle.
749
 *
750
 * @param[out] buffer   A pointer to a buffer to place the string.
751
 *
752
 * @param[in]  len      The size of the buffer in bytes. If the string is bigger
753
 *                      than len-1, only len-1 bytes are read and the string is
754
 *                      null-terminated.
755
 *
756
 * @returns             SSH_OK if the string was read, SSH_ERROR if an error
757
 *                      occurred while reading.
758
 */
759
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
760
0
{
761
0
    size_t read = 0;
762
0
    int err = SSH_OK;
763
764
0
    if (scp == NULL) {
765
0
        return SSH_ERROR;
766
0
    }
767
768
0
    while (read < len - 1) {
769
0
        err = ssh_channel_read(scp->channel, &buffer[read], 1, 0);
770
0
        if (err == SSH_ERROR) {
771
0
            break;
772
0
        }
773
774
0
        if (err == 0) {
775
0
            ssh_set_error(scp->session, SSH_FATAL,
776
0
                          "End of file while reading string");
777
0
            err = SSH_ERROR;
778
0
            break;
779
0
        }
780
781
0
        if (err == SSH_AGAIN) {
782
0
            ssh_set_error(scp->session,
783
0
                          SSH_FATAL,
784
0
                          "SCP: ssh_channel_read timeout");
785
0
            err = SSH_ERROR;
786
0
            break;
787
0
        }
788
789
0
        read++;
790
0
        if (buffer[read - 1] == '\n') {
791
0
            break;
792
0
        }
793
0
    }
794
795
0
    buffer[read] = 0;
796
0
    return err;
797
0
}
798
799
/**
800
 * @deprecated Please use SFTP instead
801
 *
802
 * @brief Wait for a scp request (file, directory).
803
 *
804
 * @returns             SSH_SCP_REQUEST_NEWFILE:       The other side is sending
805
 *                                                     a file
806
 *                      SSH_SCP_REQUEST_NEWDIR:  The other side is sending
807
 *                                                     a directory
808
 *                      SSH_SCP_REQUEST_ENDDIR: The other side has
809
 *                                                     finished with the current
810
 *                                                     directory
811
 *                      SSH_SCP_REQUEST_WARNING: The other side sent us a warning
812
 *                      SSH_SCP_REQUEST_EOF: The other side finished sending us
813
 *                                           files and data.
814
 *                      SSH_ERROR:                     Some error happened
815
 *
816
 * @see ssh_scp_read()
817
 * @see ssh_scp_deny_request()
818
 * @see ssh_scp_accept_request()
819
 * @see ssh_scp_request_get_warning()
820
 */
821
int ssh_scp_pull_request(ssh_scp scp)
822
0
{
823
0
    char buffer[PATH_MAX] = {0};
824
0
    char *mode = NULL;
825
0
    char *p, *tmp;
826
0
    uint64_t size;
827
0
    char *name = NULL;
828
0
    int rc;
829
830
0
    if (scp == NULL) {
831
0
        return SSH_ERROR;
832
0
    }
833
834
0
    if (scp->state != SSH_SCP_READ_INITED) {
835
0
        ssh_set_error(scp->session, SSH_FATAL,
836
0
                      "ssh_scp_pull_request called under invalid state");
837
0
        return SSH_ERROR;
838
0
    }
839
840
0
    rc = ssh_scp_read_string(scp, buffer, sizeof(buffer));
841
0
    if (rc == SSH_ERROR) {
842
0
        if (ssh_channel_is_eof(scp->channel)) {
843
0
            scp->state = SSH_SCP_TERMINATED;
844
0
            return SSH_SCP_REQUEST_EOF;
845
0
        }
846
0
        return rc;
847
0
    }
848
849
0
    p = strchr(buffer, '\n');
850
0
    if (p != NULL) {
851
0
        *p = '\0';
852
0
    }
853
854
0
    SSH_LOG(SSH_LOG_DEBUG, "Received SCP request: '%s'", buffer);
855
0
    switch(buffer[0]) {
856
0
    case 'C':
857
        /* File */
858
0
    case 'D':
859
        /* Directory */
860
0
        p = strchr(buffer, ' ');
861
0
        if (p == NULL) {
862
0
            goto error;
863
0
        }
864
0
        *p = '\0';
865
0
        p++;
866
        //mode = strdup(&buffer[1]);
867
0
        scp->request_mode = ssh_scp_integer_mode(&buffer[1]);
868
0
        tmp = p;
869
0
        p = strchr(p, ' ');
870
0
        if (p == NULL) {
871
0
            goto error;
872
0
        }
873
0
        *p = 0;
874
0
        size = strtoull(tmp, NULL, 10);
875
0
        p++;
876
0
        name = strdup(p);
877
        /* Catch invalid name:
878
         *  - empty ones
879
         *  - containing any forward slash -- directory traversal handled
880
         *    differently
881
         *  - special names "." and ".." referring to the current and parent
882
         *    directories -- they are not expected either
883
         */
884
0
        if (name == NULL || name[0] == '\0' || strchr(name, '/') ||
885
0
            strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
886
0
            ssh_set_error(scp->session,
887
0
                          SSH_FATAL,
888
0
                          "Received invalid filename: %s",
889
0
                          name == NULL ? "<NULL>" : name);
890
0
            SAFE_FREE(name);
891
0
            goto error;
892
0
        }
893
0
        SAFE_FREE(scp->request_name);
894
0
        scp->request_name = name;
895
0
        if (buffer[0] == 'C') {
896
0
            scp->filelen = size;
897
0
            scp->request_type = SSH_SCP_REQUEST_NEWFILE;
898
0
        } else {
899
0
            scp->filelen = '0';
900
0
            scp->request_type = SSH_SCP_REQUEST_NEWDIR;
901
0
        }
902
0
        scp->state = SSH_SCP_READ_REQUESTED;
903
0
        scp->processed = 0;
904
0
        return scp->request_type;
905
0
        break;
906
0
    case 'E':
907
0
        scp->request_type = SSH_SCP_REQUEST_ENDDIR;
908
0
        ssh_channel_write(scp->channel, "", 1);
909
0
        return scp->request_type;
910
0
    case 0x1:
911
0
        ssh_set_error(scp->session, SSH_REQUEST_DENIED,
912
0
                      "SCP: Warning: %s", &buffer[1]);
913
0
        scp->request_type = SSH_SCP_REQUEST_WARNING;
914
0
        SAFE_FREE(scp->warning);
915
0
        scp->warning = strdup(&buffer[1]);
916
0
        return scp->request_type;
917
0
    case 0x2:
918
0
        ssh_set_error(scp->session, SSH_FATAL,
919
0
                      "SCP: Error: %s", &buffer[1]);
920
0
        return SSH_ERROR;
921
0
    case 'T':
922
        /* Timestamp */
923
0
    default:
924
0
        ssh_set_error(scp->session, SSH_FATAL,
925
0
                      "Unhandled message: (%d)%s", buffer[0], buffer);
926
0
        return SSH_ERROR;
927
0
    }
928
929
    /* a parsing error occurred */
930
0
error:
931
0
    SAFE_FREE(name);
932
0
    SAFE_FREE(mode);
933
0
    ssh_set_error(scp->session, SSH_FATAL,
934
0
                  "Parsing error while parsing message: %s", buffer);
935
0
    return SSH_ERROR;
936
0
}
937
938
/**
939
 * @deprecated Please use SFTP instead
940
 *
941
 * @brief Deny the transfer of a file or creation of a directory coming from the
942
 * remote party.
943
 *
944
 * @param[in]  scp      The scp handle.
945
 * @param[in]  reason   A nul-terminated string with a human-readable
946
 *                      explanation of the deny.
947
 *
948
 * @returns             SSH_OK if the message was sent, SSH_ERROR if the sending
949
 *                      the message failed, or sending it in a bad state.
950
 */
951
int ssh_scp_deny_request(ssh_scp scp, const char *reason)
952
0
{
953
0
    char *buffer = NULL;
954
0
    size_t len;
955
0
    int rc;
956
957
0
    if (scp == NULL) {
958
0
        return SSH_ERROR;
959
0
    }
960
961
0
    if (scp->state != SSH_SCP_READ_REQUESTED) {
962
0
        ssh_set_error(scp->session, SSH_FATAL,
963
0
                      "ssh_scp_deny_request called under invalid state");
964
0
        return SSH_ERROR;
965
0
    }
966
967
0
    len = strlen(reason) + 3;
968
0
    buffer = malloc(len);
969
0
    if (buffer == NULL) {
970
0
        return SSH_ERROR;
971
0
    }
972
973
0
    snprintf(buffer, len, "%c%s\n", 2, reason);
974
0
    rc = ssh_channel_write(scp->channel, buffer, len - 1);
975
0
    free(buffer);
976
0
    if (rc == SSH_ERROR) {
977
0
        return SSH_ERROR;
978
0
    }
979
980
0
    else {
981
0
        scp->state = SSH_SCP_READ_INITED;
982
0
        return SSH_OK;
983
0
    }
984
0
}
985
986
/**
987
 * @deprecated Please use SFTP instead
988
 *
989
 * @brief Accepts transfer of a file or creation of a directory coming from the
990
 * remote party.
991
 *
992
 * @param[in]  scp      The scp handle.
993
 *
994
 * @returns             SSH_OK if the message was sent, SSH_ERROR if sending the
995
 *                      message failed, or sending it in a bad state.
996
 */
997
int ssh_scp_accept_request(ssh_scp scp)
998
0
{
999
0
    char buffer[] = {0x00};
1000
0
    int rc;
1001
0
    if (scp == NULL) {
1002
0
        return SSH_ERROR;
1003
0
    }
1004
1005
0
    if (scp->state != SSH_SCP_READ_REQUESTED) {
1006
0
        ssh_set_error(scp->session, SSH_FATAL,
1007
0
                      "ssh_scp_deny_request called under invalid state");
1008
0
        return SSH_ERROR;
1009
0
    }
1010
1011
0
    rc = ssh_channel_write(scp->channel, buffer, 1);
1012
0
    if (rc == SSH_ERROR) {
1013
0
        return SSH_ERROR;
1014
0
    }
1015
1016
0
    if (scp->request_type == SSH_SCP_REQUEST_NEWFILE) {
1017
0
        scp->state = SSH_SCP_READ_READING;
1018
0
    } else {
1019
0
        scp->state = SSH_SCP_READ_INITED;
1020
0
    }
1021
1022
0
    return SSH_OK;
1023
0
}
1024
1025
/**
1026
 * @deprecated Please use SFTP instead
1027
 *
1028
 * @brief Read from a remote scp file
1029
 *
1030
 * @param[in]  scp      The scp handle.
1031
 *
1032
 * @param[in]  buffer   The destination buffer.
1033
 *
1034
 * @param[in]  size     The size of the buffer.
1035
 *
1036
 * @returns             The number of bytes read, SSH_ERROR if an error occurred
1037
 *                      while reading.
1038
 */
1039
int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
1040
0
{
1041
0
    int rc;
1042
0
    int code;
1043
1044
0
    if (scp == NULL) {
1045
0
        return SSH_ERROR;
1046
0
    }
1047
1048
0
    if (scp->state == SSH_SCP_READ_REQUESTED &&
1049
0
        scp->request_type == SSH_SCP_REQUEST_NEWFILE)
1050
0
    {
1051
0
        rc = ssh_scp_accept_request(scp);
1052
0
        if (rc == SSH_ERROR) {
1053
0
            return rc;
1054
0
        }
1055
0
    }
1056
1057
0
    if (scp->state != SSH_SCP_READ_READING) {
1058
0
        ssh_set_error(scp->session, SSH_FATAL,
1059
0
                      "ssh_scp_read called under invalid state");
1060
0
        return SSH_ERROR;
1061
0
    }
1062
1063
0
    if (scp->processed + size > scp->filelen) {
1064
0
        size = (size_t) (scp->filelen - scp->processed);
1065
0
    }
1066
1067
0
    if (size > 65536) {
1068
0
        size = 65536; /* avoid too large reads */
1069
0
    }
1070
1071
0
    rc = ssh_channel_read(scp->channel, buffer, size, 0);
1072
0
    if (rc == SSH_ERROR) {
1073
0
        scp->state = SSH_SCP_ERROR;
1074
0
        return SSH_ERROR;
1075
0
    }
1076
0
    if (rc == SSH_AGAIN) {
1077
0
        ssh_set_error(scp->session, SSH_FATAL, "SCP: ssh_channel_read timeout");
1078
0
        scp->state = SSH_SCP_ERROR;
1079
0
        return SSH_ERROR;
1080
0
    }
1081
0
    scp->processed += rc;
1082
1083
    /* Check if we arrived at end of file */
1084
0
    if (scp->processed == scp->filelen) {
1085
0
        scp->processed = scp->filelen = 0;
1086
0
        ssh_channel_write(scp->channel, "", 1);
1087
0
        code = ssh_scp_response(scp, NULL);
1088
0
        if (code == 0) {
1089
0
            scp->state = SSH_SCP_READ_INITED;
1090
0
            return rc;
1091
0
        }
1092
0
        if (code == 1) {
1093
0
            scp->state = SSH_SCP_READ_INITED;
1094
0
            return SSH_ERROR;
1095
0
        }
1096
0
        scp->state = SSH_SCP_ERROR;
1097
0
        return SSH_ERROR;
1098
0
    }
1099
1100
0
    return rc;
1101
0
}
1102
1103
/**
1104
 * @deprecated Please use SFTP instead
1105
 *
1106
 * @brief Get the name of the directory or file being pushed from the other
1107
 * party.
1108
 *
1109
 * @returns             The file name, NULL on error. The string should not be
1110
 *                      freed.
1111
 */
1112
const char *ssh_scp_request_get_filename(ssh_scp scp)
1113
0
{
1114
0
    if (scp == NULL) {
1115
0
        return NULL;
1116
0
    }
1117
1118
0
    return scp->request_name;
1119
0
}
1120
1121
/**
1122
 * @deprecated Please use SFTP instead
1123
 *
1124
 * @brief Get the permissions of the directory or file being pushed from the
1125
 * other party.
1126
 *
1127
 * @returns             The UNIX permission, e.g 0644, -1 on error.
1128
 */
1129
int ssh_scp_request_get_permissions(ssh_scp scp)
1130
0
{
1131
0
    if (scp == NULL) {
1132
0
        return -1;
1133
0
    }
1134
1135
0
    return scp->request_mode;
1136
0
}
1137
1138
/**
1139
 * @deprecated Please use SFTP instead
1140
 *
1141
 * @brief Get the size of the file being pushed from the other party.
1142
 *
1143
 * @returns             The numeric size of the file being read.
1144
 * @warning             The real size may not fit in a 32 bits field and may
1145
 *                      be truncated.
1146
 * @see ssh_scp_request_get_size64()
1147
 */
1148
size_t ssh_scp_request_get_size(ssh_scp scp)
1149
0
{
1150
0
    if (scp == NULL) {
1151
0
        return 0;
1152
0
    }
1153
0
    return (size_t)scp->filelen;
1154
0
}
1155
1156
/**
1157
 * @deprecated Please use SFTP instead
1158
 *
1159
 * @brief Get the size of the file being pushed from the other party.
1160
 *
1161
 * @returns             The numeric size of the file being read.
1162
 */
1163
uint64_t ssh_scp_request_get_size64(ssh_scp scp)
1164
0
{
1165
0
    if (scp == NULL) {
1166
0
        return 0;
1167
0
    }
1168
0
    return scp->filelen;
1169
0
}
1170
1171
/**
1172
 * @deprecated Please use SFTP instead
1173
 *
1174
 * @brief Convert a scp text mode to an integer.
1175
 *
1176
 * @param[in]  mode     The mode to convert, e.g. "0644".
1177
 *
1178
 * @returns             An integer value, e.g. 420 for "0644".
1179
 */
1180
int ssh_scp_integer_mode(const char *mode)
1181
0
{
1182
0
    int value = strtoul(mode, NULL, 8) & 0xffff;
1183
0
    return value;
1184
0
}
1185
1186
/**
1187
 * @deprecated Please use SFTP instead
1188
 *
1189
 * @brief Convert a unix mode into a scp string.
1190
 *
1191
 * @param[in]  mode     The mode to convert, e.g. 420 or 0644.
1192
 *
1193
 * @returns             A pointer to a malloc'ed string containing the scp mode,
1194
 *                      e.g. "0644".
1195
 */
1196
char *ssh_scp_string_mode(int mode)
1197
0
{
1198
0
    char buffer[16] = {0};
1199
0
    snprintf(buffer, sizeof(buffer), "%.4o", mode);
1200
0
    return strdup(buffer);
1201
0
}
1202
1203
/**
1204
 * @deprecated Please use SFTP instead
1205
 *
1206
 * @brief Get the warning string from a scp handle.
1207
 *
1208
 * @param[in]  scp      The scp handle.
1209
 *
1210
 * @returns             A warning string, or NULL on error. The string should
1211
 *                      not be freed.
1212
 */
1213
const char *ssh_scp_request_get_warning(ssh_scp scp)
1214
0
{
1215
0
    if (scp == NULL) {
1216
0
        return NULL;
1217
0
    }
1218
1219
0
    return scp->warning;
1220
0
}
1221
1222
/** @} */