Coverage Report

Created: 2026-04-06 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/sftp_common.c
Line
Count
Source
1
/*
2
 * sftp_common.c - Secure FTP functions which are private and are used
3
 *                 internally by other sftp api functions spread across
4
 *                 various source files.
5
 *
6
 * This file is part of the SSH Library
7
 *
8
 * Copyright (c) 2005-2008 by Aris Adamantiadis
9
 * Copyright (c) 2008-2018 by Andreas Schneider <asn@cryptomilk.org>
10
 *
11
 * The SSH Library is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU Lesser General Public License as published by
13
 * the Free Software Foundation; either version 2.1 of the License, or (at your
14
 * option) any later version.
15
 *
16
 * The SSH Library is distributed in the hope that it will be useful, but
17
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
19
 * License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public License
22
 * along with the SSH Library; see the file COPYING.  If not, write to
23
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24
 * MA 02111-1307, USA.
25
 */
26
27
#include "config.h"
28
29
#include <ctype.h>
30
31
#include "libssh/sftp.h"
32
#include "libssh/sftp_priv.h"
33
#include "libssh/buffer.h"
34
#include "libssh/session.h"
35
#include "libssh/bytearray.h"
36
37
#ifdef WITH_SFTP
38
39
/* Buffer size maximum is 256M */
40
0
#define SFTP_PACKET_SIZE_MAX 0x10000000
41
42
sftp_packet sftp_packet_read(sftp_session sftp)
43
0
{
44
0
    uint8_t tmpbuf[4];
45
0
    uint8_t *buffer = NULL;
46
0
    sftp_packet packet = sftp->read_packet;
47
0
    uint32_t size;
48
0
    int nread;
49
0
    bool is_eof;
50
0
    int rc;
51
52
0
    packet->sftp = sftp;
53
54
    /*
55
     * If the packet has a payload, then just reinit the buffer, otherwise
56
     * allocate a new one.
57
     */
58
0
    if (packet->payload != NULL) {
59
0
        rc = ssh_buffer_reinit(packet->payload);
60
0
        if (rc != 0) {
61
0
            ssh_set_error_oom(sftp->session);
62
0
            sftp_set_error(sftp, SSH_FX_FAILURE);
63
0
            return NULL;
64
0
        }
65
0
    } else {
66
0
        packet->payload = ssh_buffer_new();
67
0
        if (packet->payload == NULL) {
68
0
            ssh_set_error_oom(sftp->session);
69
0
            sftp_set_error(sftp, SSH_FX_FAILURE);
70
0
            return NULL;
71
0
        }
72
0
    }
73
74
0
    nread = 0;
75
0
    do {
76
0
        int s;
77
78
        /* read from channel until 4 bytes have been read or an error occurs */
79
0
        s = ssh_channel_read(sftp->channel, tmpbuf + nread, 4 - nread, 0);
80
0
        if (s < 0) {
81
0
            goto error;
82
0
        } else if (s == 0) {
83
0
            is_eof = ssh_channel_is_eof(sftp->channel);
84
0
            if (is_eof) {
85
0
                ssh_set_error(sftp->session,
86
0
                              SSH_FATAL,
87
0
                              "Received EOF while reading sftp packet size");
88
0
                sftp_set_error(sftp, SSH_FX_EOF);
89
0
                goto error;
90
0
            } else {
91
0
                ssh_set_error(sftp->session,
92
0
                              SSH_FATAL,
93
0
                              "Timeout while reading sftp packet size");
94
0
                sftp_set_error(sftp, SSH_FX_FAILURE);
95
0
                goto error;
96
0
            }
97
0
        } else {
98
0
            nread += s;
99
0
        }
100
0
    } while (nread < 4);
101
102
0
    size = PULL_BE_U32(tmpbuf, 0);
103
0
    if (size == 0 || size > SFTP_PACKET_SIZE_MAX) {
104
0
        ssh_set_error(sftp->session, SSH_FATAL, "Invalid sftp packet size!");
105
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
106
0
        goto error;
107
0
    }
108
109
0
    do {
110
0
        nread = ssh_channel_read(sftp->channel, tmpbuf, 1, 0);
111
0
        if (nread < 0) {
112
0
            goto error;
113
0
        } else if (nread == 0) {
114
0
            is_eof = ssh_channel_is_eof(sftp->channel);
115
0
            if (is_eof) {
116
0
                ssh_set_error(sftp->session,
117
0
                              SSH_FATAL,
118
0
                              "Received EOF while reading sftp packet type");
119
0
                sftp_set_error(sftp, SSH_FX_EOF);
120
0
                goto error;
121
0
            } else {
122
0
                ssh_set_error(sftp->session,
123
0
                              SSH_FATAL,
124
0
                              "Timeout while reading sftp packet type");
125
0
                sftp_set_error(sftp, SSH_FX_FAILURE);
126
0
                goto error;
127
0
            }
128
0
        }
129
0
    } while (nread < 1);
130
131
0
    packet->type = tmpbuf[0];
132
133
    /* Remove the packet type size */
134
0
    size -= sizeof(uint8_t);
135
136
    /* Allocate the receive buffer from payload */
137
0
    buffer = ssh_buffer_allocate(packet->payload, size);
138
0
    if (buffer == NULL) {
139
0
        ssh_set_error_oom(sftp->session);
140
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
141
0
        goto error;
142
0
    }
143
0
    while (size > 0 && size < SFTP_PACKET_SIZE_MAX) {
144
0
        nread = ssh_channel_read(sftp->channel, buffer, size, 0);
145
0
        if (nread < 0) {
146
            /* TODO: check if there are cases where an error needs to be set here */
147
0
            goto error;
148
0
        }
149
150
0
        if (nread > 0) {
151
0
            buffer += nread;
152
0
            size -= nread;
153
0
        } else { /* nread == 0 */
154
            /* Retry the reading unless the remote was closed */
155
0
            is_eof = ssh_channel_is_eof(sftp->channel);
156
0
            if (is_eof) {
157
0
                ssh_set_error(sftp->session,
158
0
                              SSH_REQUEST_DENIED,
159
0
                              "Received EOF while reading sftp packet");
160
0
                sftp_set_error(sftp, SSH_FX_EOF);
161
0
                goto error;
162
0
            } else {
163
0
                ssh_set_error(sftp->session,
164
0
                              SSH_FATAL,
165
0
                              "Timeout while reading sftp packet");
166
0
                sftp_set_error(sftp, SSH_FX_FAILURE);
167
0
                goto error;
168
0
            }
169
0
        }
170
0
    }
171
172
0
    return packet;
173
0
error:
174
0
    ssh_buffer_reinit(packet->payload);
175
0
    return NULL;
176
0
}
177
178
int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload)
179
0
{
180
0
    uint8_t header[5] = {0};
181
0
    uint32_t payload_size;
182
0
    int size;
183
0
    int rc;
184
185
    /* Add size of type */
186
0
    payload_size = ssh_buffer_get_len(payload) + sizeof(uint8_t);
187
0
    PUSH_BE_U32(header, 0, payload_size);
188
0
    PUSH_BE_U8(header, 4, type);
189
190
0
    rc = ssh_buffer_prepend_data(payload, header, sizeof(header));
191
0
    if (rc < 0) {
192
0
        ssh_set_error_oom(sftp->session);
193
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
194
0
        return -1;
195
0
    }
196
197
0
    size = ssh_channel_write(sftp->channel,
198
0
                             ssh_buffer_get(payload),
199
0
                             ssh_buffer_get_len(payload));
200
0
    if (size < 0) {
201
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
202
0
        return -1;
203
0
    }
204
205
0
    if ((uint32_t)size != ssh_buffer_get_len(payload)) {
206
0
        SSH_LOG(SSH_LOG_PACKET,
207
0
                "Had to write %" PRIu32 " bytes, wrote only %d",
208
0
                ssh_buffer_get_len(payload),
209
0
                size);
210
0
    }
211
212
0
    return size;
213
0
}
214
215
void sftp_packet_free(sftp_packet packet)
216
0
{
217
0
    if (packet == NULL) {
218
0
        return;
219
0
    }
220
221
0
    SSH_BUFFER_FREE(packet->payload);
222
0
    free(packet);
223
0
}
224
225
int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr)
226
0
{
227
0
    uint32_t flags = (attr ? attr->flags : 0);
228
0
    int rc;
229
230
0
    flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID |
231
0
              SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
232
233
0
    rc = ssh_buffer_pack(buffer, "d", flags);
234
0
    if (rc != SSH_OK) {
235
0
        return -1;
236
0
    }
237
238
0
    if (attr != NULL) {
239
0
        if (flags & SSH_FILEXFER_ATTR_SIZE) {
240
0
            rc = ssh_buffer_pack(buffer, "q", attr->size);
241
0
            if (rc != SSH_OK) {
242
0
                return -1;
243
0
            }
244
0
        }
245
246
0
        if (flags & SSH_FILEXFER_ATTR_UIDGID) {
247
0
            rc = ssh_buffer_pack(buffer, "dd", attr->uid, attr->gid);
248
0
            if (rc != SSH_OK) {
249
0
                return -1;
250
0
            }
251
0
        }
252
253
0
        if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
254
0
            rc = ssh_buffer_pack(buffer, "d", attr->permissions);
255
0
            if (rc != SSH_OK) {
256
0
                return -1;
257
0
            }
258
0
        }
259
260
0
        if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
261
0
            rc = ssh_buffer_pack(buffer, "dd", attr->atime, attr->mtime);
262
0
            if (rc != SSH_OK) {
263
0
                return -1;
264
0
            }
265
0
        }
266
0
    }
267
268
0
    return 0;
269
0
}
270
271
/*
272
 * Parse the attributes from a payload from some messages. It is coded on
273
 * baselines from the protocol version 4.
274
 * This code is more or less dead but maybe we will need it in the future.
275
 */
276
static sftp_attributes sftp_parse_attr_4(sftp_session sftp,
277
                                         ssh_buffer buf,
278
                                         int expectnames)
279
1.85k
{
280
1.85k
    sftp_attributes attr = NULL;
281
1.85k
    ssh_string owner = NULL;
282
1.85k
    ssh_string group = NULL;
283
1.85k
    uint32_t flags = 0;
284
1.85k
    int ok = 0;
285
286
    /* unused member variable */
287
1.85k
    (void) expectnames;
288
289
1.85k
    attr = calloc(1, sizeof(struct sftp_attributes_struct));
290
1.85k
    if (attr == NULL) {
291
0
        ssh_set_error_oom(sftp->session);
292
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
293
0
        return NULL;
294
0
    }
295
296
    /* This isn't really a loop, but it is like a try..catch.. */
297
1.85k
    do {
298
1.85k
        if (ssh_buffer_get_u32(buf, &flags) != 4) {
299
26
            break;
300
26
        }
301
302
1.83k
        flags = ntohl(flags);
303
1.83k
        attr->flags = flags;
304
305
1.83k
        if (flags & SSH_FILEXFER_ATTR_SIZE) {
306
442
            if (ssh_buffer_get_u64(buf, &attr->size) != 8) {
307
46
                break;
308
46
            }
309
396
            attr->size = ntohll(attr->size);
310
396
        }
311
312
1.78k
        if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
313
672
            owner = ssh_buffer_get_ssh_string(buf);
314
672
            if (owner == NULL) {
315
338
                break;
316
338
            }
317
334
            attr->owner = ssh_string_to_char(owner);
318
334
            SSH_STRING_FREE(owner);
319
334
            if (attr->owner == NULL) {
320
0
                break;
321
0
            }
322
323
334
            group = ssh_buffer_get_ssh_string(buf);
324
334
            if (group == NULL) {
325
140
                break;
326
140
            }
327
194
            attr->group = ssh_string_to_char(group);
328
194
            SSH_STRING_FREE(group);
329
194
            if (attr->group == NULL) {
330
0
                break;
331
0
            }
332
194
        }
333
334
1.30k
        if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
335
304
            if (ssh_buffer_get_u32(buf, &attr->permissions) != 4) {
336
40
                break;
337
40
            }
338
264
            attr->permissions = ntohl(attr->permissions);
339
340
            /* FIXME on windows! */
341
264
            switch (attr->permissions & SSH_S_IFMT) {
342
14
            case SSH_S_IFSOCK:
343
18
            case SSH_S_IFBLK:
344
26
            case SSH_S_IFCHR:
345
34
            case SSH_S_IFIFO:
346
34
                attr->type = SSH_FILEXFER_TYPE_SPECIAL;
347
34
                break;
348
0
            case SSH_S_IFLNK:
349
0
                attr->type = SSH_FILEXFER_TYPE_SYMLINK;
350
0
                break;
351
6
            case SSH_S_IFREG:
352
6
                attr->type = SSH_FILEXFER_TYPE_REGULAR;
353
6
                break;
354
4
            case SSH_S_IFDIR:
355
4
                attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
356
4
                break;
357
220
            default:
358
220
                attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
359
220
                break;
360
264
            }
361
264
        }
362
363
1.26k
        if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
364
252
            if (ssh_buffer_get_u64(buf, &attr->atime64) != 8) {
365
38
                break;
366
38
            }
367
214
            attr->atime64 = ntohll(attr->atime64);
368
369
214
            if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
370
146
                if (ssh_buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
371
6
                    break;
372
6
                }
373
140
                attr->atime_nseconds = ntohl(attr->atime_nseconds);
374
140
            }
375
214
        }
376
377
1.22k
        if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
378
254
            if (ssh_buffer_get_u64(buf, &attr->createtime) != 8) {
379
28
                break;
380
28
            }
381
226
            attr->createtime = ntohll(attr->createtime);
382
383
226
            if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
384
160
                if (ssh_buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
385
2
                    break;
386
2
                }
387
158
                attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
388
158
            }
389
226
        }
390
391
1.19k
        if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
392
236
            if (ssh_buffer_get_u64(buf, &attr->mtime64) != 8) {
393
36
                break;
394
36
            }
395
200
            attr->mtime64 = ntohll(attr->mtime64);
396
397
200
            if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
398
154
                if (ssh_buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
399
4
                    break;
400
4
                }
401
150
                attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
402
150
            }
403
200
        }
404
405
1.15k
        if (flags & SSH_FILEXFER_ATTR_ACL) {
406
216
            if ((attr->acl = ssh_buffer_get_ssh_string(buf)) == NULL) {
407
86
                break;
408
86
            }
409
216
        }
410
411
1.06k
        if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
412
818
            if (ssh_buffer_get_u32(buf,&attr->extended_count) != 4) {
413
168
                break;
414
168
            }
415
650
            attr->extended_count = ntohl(attr->extended_count);
416
417
392k
            while (attr->extended_count &&
418
392k
                   (attr->extended_type = ssh_buffer_get_ssh_string(buf)) &&
419
392k
                   (attr->extended_data = ssh_buffer_get_ssh_string(buf))) {
420
391k
                attr->extended_count--;
421
                /* just ignore the extensions -- we can't interpret them */
422
391k
                SSH_STRING_FREE(attr->extended_type);
423
391k
                SSH_STRING_FREE(attr->extended_data);
424
391k
            }
425
426
650
            if (attr->extended_count) {
427
578
                break;
428
578
            }
429
650
        }
430
322
        ok = 1;
431
322
    } while (0);
432
433
1.85k
    if (ok == 0) {
434
        /* break issued somewhere */
435
1.53k
        SSH_STRING_FREE(attr->acl);
436
1.53k
        SSH_STRING_FREE(attr->extended_type);
437
1.53k
        SSH_STRING_FREE(attr->extended_data);
438
1.53k
        SAFE_FREE(attr->owner);
439
1.53k
        SAFE_FREE(attr->group);
440
1.53k
        SAFE_FREE(attr);
441
442
1.53k
        ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
443
444
1.53k
        return NULL;
445
1.53k
    }
446
447
322
    return attr;
448
1.85k
}
449
450
enum sftp_longname_field_e {
451
    SFTP_LONGNAME_PERM = 0,
452
    SFTP_LONGNAME_FIXME,
453
    SFTP_LONGNAME_OWNER,
454
    SFTP_LONGNAME_GROUP,
455
    SFTP_LONGNAME_SIZE,
456
    SFTP_LONGNAME_DATE,
457
    SFTP_LONGNAME_TIME,
458
    SFTP_LONGNAME_NAME,
459
};
460
461
static char * sftp_parse_longname(const char *longname,
462
                                  enum sftp_longname_field_e longname_field)
463
0
{
464
0
    const char *p = NULL, *q = NULL;
465
0
    size_t len, field = 0;
466
467
0
    if (longname == NULL || longname_field < SFTP_LONGNAME_PERM ||
468
0
        longname_field > SFTP_LONGNAME_NAME) {
469
0
        return NULL;
470
0
    }
471
472
0
    p = longname;
473
    /*
474
     * Find the beginning of the field which is specified
475
     * by sftp_longname_field_e.
476
     */
477
0
    while (*p != '\0' && field != longname_field) {
478
0
        if (isspace(*p)) {
479
0
            field++;
480
0
            p++;
481
0
            while (*p != '\0' && isspace(*p)) {
482
0
                p++;
483
0
            }
484
0
        } else {
485
0
            p++;
486
0
        }
487
0
    }
488
489
    /* If we reached NULL before we got our field fail */
490
0
    if (field != longname_field) {
491
0
        return NULL;
492
0
    }
493
494
0
    q = p;
495
0
    while (*q != '\0' && !isspace(*q)) {
496
0
        q++;
497
0
    }
498
499
0
    len = q - p;
500
501
0
    return strndup(p, len);
502
0
}
503
504
/* sftp version 0-3 code. It is different from the v4 */
505
/* maybe a paste of the draft is better than the code */
506
/*
507
        uint32   flags
508
        uint64   size           present only if flag SSH_FILEXFER_ATTR_SIZE
509
        uint32   uid            present only if flag SSH_FILEXFER_ATTR_UIDGID
510
        uint32   gid            present only if flag SSH_FILEXFER_ATTR_UIDGID
511
        uint32   permissions    present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
512
        uint32   atime          present only if flag SSH_FILEXFER_ACMODTIME
513
        uint32   mtime          present only if flag SSH_FILEXFER_ACMODTIME
514
        uint32   extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
515
        string   extended_type
516
        string   extended_data
517
        ...      more extended data (extended_type - extended_data pairs),
518
                   so that number of pairs equals extended_count              */
519
static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
520
                                         ssh_buffer buf,
521
                                         int expectname)
522
1.85k
{
523
1.85k
    sftp_attributes attr;
524
1.85k
    int rc;
525
526
1.85k
    attr = calloc(1, sizeof(struct sftp_attributes_struct));
527
1.85k
    if (attr == NULL) {
528
0
        ssh_set_error_oom(sftp->session);
529
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
530
0
        return NULL;
531
0
    }
532
533
1.85k
    if (expectname) {
534
929
        rc = ssh_buffer_unpack(buf, "ss",
535
929
                               &attr->name,
536
929
                               &attr->longname);
537
929
        if (rc != SSH_OK){
538
854
            goto error;
539
854
        }
540
75
        SSH_LOG(SSH_LOG_DEBUG, "Name: %s", attr->name);
541
542
        /* Set owner and group if we talk to openssh and have the longname */
543
75
        if (ssh_get_openssh_version(sftp->session)) {
544
0
            attr->owner = sftp_parse_longname(attr->longname,
545
0
                                              SFTP_LONGNAME_OWNER);
546
0
            if (attr->owner == NULL) {
547
0
                goto error;
548
0
            }
549
550
0
            attr->group = sftp_parse_longname(attr->longname,
551
0
                                              SFTP_LONGNAME_GROUP);
552
0
            if (attr->group == NULL) {
553
0
                goto error;
554
0
            }
555
0
        }
556
75
    }
557
558
1.00k
    rc = ssh_buffer_unpack(buf, "d", &attr->flags);
559
1.00k
    if (rc != SSH_OK){
560
21
        goto error;
561
21
    }
562
983
    SSH_LOG(SSH_LOG_DEBUG, "Flags: %.8" PRIx32, attr->flags);
563
564
983
    if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
565
255
        rc = ssh_buffer_unpack(buf, "q", &attr->size);
566
255
        if(rc != SSH_OK) {
567
30
            goto error;
568
30
        }
569
225
        SSH_LOG(SSH_LOG_DEBUG, "Size: %" PRIu64, (uint64_t)attr->size);
570
225
    }
571
572
953
    if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {
573
327
        rc = ssh_buffer_unpack(buf, "dd",
574
327
                               &attr->uid,
575
327
                               &attr->gid);
576
327
        if (rc != SSH_OK) {
577
67
            goto error;
578
67
        }
579
327
    }
580
581
886
    if (attr->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
582
281
        rc = ssh_buffer_unpack(buf, "d", &attr->permissions);
583
281
        if (rc != SSH_OK) {
584
25
            goto error;
585
25
        }
586
587
256
        switch (attr->permissions & SSH_S_IFMT) {
588
8
        case SSH_S_IFSOCK:
589
10
        case SSH_S_IFBLK:
590
15
        case SSH_S_IFCHR:
591
24
        case SSH_S_IFIFO:
592
24
            attr->type = SSH_FILEXFER_TYPE_SPECIAL;
593
24
            break;
594
6
        case SSH_S_IFLNK:
595
6
            attr->type = SSH_FILEXFER_TYPE_SYMLINK;
596
6
            break;
597
4
        case SSH_S_IFREG:
598
4
            attr->type = SSH_FILEXFER_TYPE_REGULAR;
599
4
            break;
600
3
        case SSH_S_IFDIR:
601
3
            attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
602
3
            break;
603
219
        default:
604
219
            attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
605
219
            break;
606
256
        }
607
256
    }
608
609
861
    if (attr->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
610
239
        rc = ssh_buffer_unpack(buf, "dd",
611
239
                               &attr->atime,
612
239
                               &attr->mtime);
613
239
        if (rc != SSH_OK) {
614
35
            goto error;
615
35
        }
616
239
    }
617
618
826
    if (attr->flags & SSH_FILEXFER_ATTR_EXTENDED) {
619
611
        rc = ssh_buffer_unpack(buf, "d", &attr->extended_count);
620
611
        if (rc != SSH_OK) {
621
81
            goto error;
622
81
        }
623
624
530
        if (attr->extended_count > 0) {
625
489
            rc = ssh_buffer_unpack(buf, "ss",
626
489
                                   &attr->extended_type,
627
489
                                   &attr->extended_data);
628
489
            if (rc != SSH_OK) {
629
235
                goto error;
630
235
            }
631
254
            attr->extended_count--;
632
254
        }
633
        /* just ignore the remaining extensions */
634
635
202k
        while (attr->extended_count > 0) {
636
202k
            ssh_string tmp1,tmp2;
637
202k
            rc = ssh_buffer_unpack(buf, "SS", &tmp1, &tmp2);
638
202k
            if (rc != SSH_OK){
639
249
                goto error;
640
249
            }
641
202k
            SAFE_FREE(tmp1);
642
202k
            SAFE_FREE(tmp2);
643
202k
            attr->extended_count--;
644
202k
        }
645
295
    }
646
647
261
    return attr;
648
649
1.59k
error:
650
1.59k
    SSH_STRING_FREE(attr->extended_type);
651
1.59k
    SSH_STRING_FREE(attr->extended_data);
652
1.59k
    SAFE_FREE(attr->name);
653
1.59k
    SAFE_FREE(attr->longname);
654
1.59k
    SAFE_FREE(attr->owner);
655
1.59k
    SAFE_FREE(attr->group);
656
1.59k
    SAFE_FREE(attr);
657
1.59k
    ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");
658
1.59k
    sftp_set_error(sftp, SSH_FX_FAILURE);
659
660
1.59k
    return NULL;
661
826
}
662
663
sftp_attributes sftp_parse_attr(sftp_session session,
664
                                ssh_buffer buf,
665
                                int expectname)
666
3.71k
{
667
3.71k
    switch (session->version) {
668
1.85k
    case 4:
669
1.85k
        return sftp_parse_attr_4(session, buf, expectname);
670
1.85k
    case 3:
671
1.85k
    case 2:
672
1.85k
    case 1:
673
1.85k
    case 0:
674
1.85k
        return sftp_parse_attr_3(session, buf, expectname);
675
0
    default:
676
0
        ssh_set_error(session->session, SSH_FATAL,
677
0
                      "Version %d unsupported by client",
678
0
                      session->server_version);
679
0
        return NULL;
680
3.71k
    }
681
682
0
    return NULL;
683
3.71k
}
684
685
void sftp_set_error(sftp_session sftp, int errnum)
686
1.59k
{
687
1.59k
    if (sftp != NULL) {
688
1.59k
        sftp->errnum = errnum;
689
1.59k
    }
690
1.59k
}
691
692
void sftp_message_free(sftp_message msg)
693
0
{
694
0
    if (msg == NULL) {
695
0
        return;
696
0
    }
697
698
0
    SSH_BUFFER_FREE(msg->payload);
699
0
    SAFE_FREE(msg);
700
0
}
701
702
static sftp_request_queue request_queue_new(sftp_message msg)
703
0
{
704
0
    sftp_request_queue queue = NULL;
705
706
0
    queue = calloc(1, sizeof(struct sftp_request_queue_struct));
707
0
    if (queue == NULL) {
708
0
        ssh_set_error_oom(msg->sftp->session);
709
0
        sftp_set_error(msg->sftp, SSH_FX_FAILURE);
710
0
        return NULL;
711
0
    }
712
713
0
    queue->message = msg;
714
715
0
    return queue;
716
0
}
717
718
static void request_queue_free(sftp_request_queue queue)
719
0
{
720
0
    if (queue == NULL) {
721
0
        return;
722
0
    }
723
724
0
    ZERO_STRUCTP(queue);
725
0
    SAFE_FREE(queue);
726
0
}
727
728
static int
729
sftp_enqueue(sftp_session sftp, sftp_message msg)
730
0
{
731
0
    sftp_request_queue queue = NULL;
732
0
    sftp_request_queue ptr;
733
734
0
    queue = request_queue_new(msg);
735
0
    if (queue == NULL) {
736
0
        return -1;
737
0
    }
738
739
0
    SSH_LOG(SSH_LOG_PACKET,
740
0
            "Queued msg id %" PRIu32 " type %d",
741
0
            msg->id, msg->packet_type);
742
743
0
    if(sftp->queue == NULL) {
744
0
        sftp->queue = queue;
745
0
    } else {
746
0
        ptr = sftp->queue;
747
0
        while(ptr->next) {
748
0
            ptr=ptr->next; /* find end of linked list */
749
0
        }
750
0
        ptr->next = queue; /* add it on bottom */
751
0
    }
752
753
0
    return 0;
754
0
}
755
756
/*
757
 * Pulls a message from the queue based on the ID.
758
 * Returns NULL if no message has been found.
759
 */
760
sftp_message sftp_dequeue(sftp_session sftp, uint32_t id)
761
0
{
762
0
    sftp_request_queue prev = NULL;
763
0
    sftp_request_queue queue;
764
0
    sftp_message msg;
765
766
0
    if(sftp->queue == NULL) {
767
0
        return NULL;
768
0
    }
769
770
0
    queue = sftp->queue;
771
0
    while (queue) {
772
0
        if (queue->message->id == id) {
773
            /* remove from queue */
774
0
            if (prev == NULL) {
775
0
                sftp->queue = queue->next;
776
0
            } else {
777
0
                prev->next = queue->next;
778
0
            }
779
0
            msg = queue->message;
780
0
            request_queue_free(queue);
781
0
            SSH_LOG(SSH_LOG_PACKET,
782
0
                    "Dequeued msg id %" PRIu32 " type %d",
783
0
                    msg->id,
784
0
                    msg->packet_type);
785
0
            return msg;
786
0
        }
787
0
        prev = queue;
788
0
        queue = queue->next;
789
0
    }
790
791
0
    return NULL;
792
0
}
793
794
static sftp_message sftp_get_message(sftp_packet packet)
795
0
{
796
0
    sftp_session sftp = packet->sftp;
797
0
    sftp_message msg = NULL;
798
0
    int rc;
799
800
0
    switch (packet->type) {
801
0
    case SSH_FXP_STATUS:
802
0
    case SSH_FXP_HANDLE:
803
0
    case SSH_FXP_DATA:
804
0
    case SSH_FXP_ATTRS:
805
0
    case SSH_FXP_NAME:
806
0
    case SSH_FXP_EXTENDED_REPLY:
807
0
        break;
808
0
    default:
809
0
        ssh_set_error(packet->sftp->session,
810
0
                      SSH_FATAL,
811
0
                      "Unknown packet type %d",
812
0
                      packet->type);
813
0
        sftp_set_error(packet->sftp, SSH_FX_FAILURE);
814
0
        return NULL;
815
0
    }
816
817
0
    msg = calloc(1, sizeof(struct sftp_message_struct));
818
0
    if (msg == NULL) {
819
0
        ssh_set_error_oom(sftp->session);
820
0
        sftp_set_error(packet->sftp, SSH_FX_FAILURE);
821
0
        return NULL;
822
0
    }
823
824
0
    msg->sftp = packet->sftp;
825
0
    msg->packet_type = packet->type;
826
827
    /* Move the payload from the packet to the message */
828
0
    msg->payload = packet->payload;
829
0
    packet->payload = NULL;
830
831
0
    rc = ssh_buffer_unpack(msg->payload, "d", &msg->id);
832
0
    if (rc != SSH_OK) {
833
0
        ssh_set_error(packet->sftp->session, SSH_FATAL,
834
0
                "Invalid packet %d: no ID", packet->type);
835
0
        sftp_message_free(msg);
836
0
        sftp_set_error(packet->sftp, SSH_FX_FAILURE);
837
0
        return NULL;
838
0
    }
839
840
0
    SSH_LOG(SSH_LOG_PACKET,
841
0
            "Packet with id %" PRIu32 " type %d",
842
0
            msg->id,
843
0
            msg->packet_type);
844
845
0
    return msg;
846
0
}
847
848
int sftp_read_and_dispatch(sftp_session sftp)
849
0
{
850
0
    sftp_packet packet = NULL;
851
0
    sftp_message msg = NULL;
852
853
0
    packet = sftp_packet_read(sftp);
854
0
    if (packet == NULL) {
855
        /* something nasty happened reading the packet */
856
0
        return -1;
857
0
    }
858
859
0
    msg = sftp_get_message(packet);
860
0
    if (msg == NULL) {
861
0
        return -1;
862
0
    }
863
864
0
    if (sftp_enqueue(sftp, msg) < 0) {
865
0
        sftp_message_free(msg);
866
0
        return -1;
867
0
    }
868
869
0
    return 0;
870
0
}
871
872
int sftp_recv_response_msg(sftp_session sftp,
873
                           uint32_t id,
874
                           bool blocking,
875
                           sftp_message *msg_ptr)
876
0
{
877
0
    sftp_message msg = NULL;
878
0
    int rc;
879
880
0
    if (sftp == NULL) {
881
0
        return SSH_ERROR;
882
0
    }
883
884
0
    if (msg_ptr == NULL) {
885
0
        ssh_set_error_invalid(sftp->session);
886
0
        sftp_set_error(sftp, SSH_FX_FAILURE);
887
0
        return SSH_ERROR;
888
0
    }
889
890
0
    SSH_LOG(SSH_LOG_PACKET,
891
0
            "Trying to receive response of request id %" PRIu32 " in %s mode",
892
0
            id,
893
0
            blocking ? "blocking" : "non-blocking");
894
895
    /*
896
     * We deliberately check the queue first for the response before
897
     * polling/blocking on the channel. The reason for this approach is
898
     * explained by the following example (And a similar scenario can occur when
899
     * the async sftp aio API is used, because it provides the control of which
900
     * responses to receive (and in what order) to the user via the
901
     * sftp_aio_wait_*() functions)
902
     *
903
     * Its possible that while this function is trying to receive some
904
     * specific response (based on request id), other responses have already
905
     * arrived (or may arrive) before that specific response on the channel. In
906
     * that case, this function would collect those other responses from the
907
     * channel, add them to the sftp response queue (using
908
     * sftp_read_and_dipatch()) and finally provide the caller with the
909
     * required specific response.
910
     *
911
     * Now, whenever the caller will call this function again to get one of
912
     * those other responses, it won't be on the channel, instead it would be
913
     * present in the queue.
914
     *
915
     * Assuming that no new response ever comes on the channel, if we don't
916
     * check the queue first and instead:
917
     * - (In non blocking mode) poll on the channel, then we'd always get 0
918
     *    bytes of data and return SSH_AGAIN.
919
     * - (In blocking mode) wait on the channel, then
920
     *   sftp_read_and_dispatch() would block infinitely by default if the
921
     *   user has not set any timeout.
922
     *
923
     * Hence checking the queue for the response first and if not found there,
924
     * polling/blocking on the channel is advised.
925
     */
926
0
    while (msg == NULL) {
927
        /*
928
         * Before trying to poll/block on the channel for data, probe the queue
929
         * to check whether the response is already present in it.
930
         */
931
0
        msg = sftp_dequeue(sftp, id);
932
0
        if (msg != NULL) {
933
0
            break;
934
0
        }
935
936
0
        if (!blocking) {
937
0
            rc = ssh_channel_poll(sftp->channel, 0);
938
0
            if (rc == SSH_ERROR) {
939
0
                sftp_set_error(sftp, SSH_FX_FAILURE);
940
0
                return SSH_ERROR;
941
0
            }
942
943
0
            if (rc == 0) {
944
                /* nothing available and we cannot block */
945
0
                return SSH_AGAIN;
946
0
            }
947
0
        }
948
949
0
        rc = sftp_read_and_dispatch(sftp);
950
0
        if (rc == -1) {
951
            /* something nasty has happened */
952
0
            return SSH_ERROR;
953
0
        }
954
0
    }
955
956
0
    *msg_ptr = msg;
957
0
    return SSH_OK;
958
0
}
959
960
sftp_status_message parse_status_msg(sftp_message msg)
961
0
{
962
0
    sftp_status_message status = NULL;
963
0
    int rc;
964
965
0
    if (msg->packet_type != SSH_FXP_STATUS) {
966
0
        ssh_set_error(msg->sftp->session, SSH_FATAL,
967
0
                      "Not a ssh_fxp_status message passed in!");
968
0
        sftp_set_error(msg->sftp, SSH_FX_BAD_MESSAGE);
969
0
        return NULL;
970
0
    }
971
972
0
    status = calloc(1, sizeof(struct sftp_status_message_struct));
973
0
    if (status == NULL) {
974
0
        ssh_set_error_oom(msg->sftp->session);
975
0
        sftp_set_error(msg->sftp, SSH_FX_FAILURE);
976
0
        return NULL;
977
0
    }
978
979
0
    status->id = msg->id;
980
0
    rc = ssh_buffer_unpack(msg->payload, "d",
981
0
                           &status->status);
982
0
    if (rc != SSH_OK) {
983
0
        SAFE_FREE(status);
984
0
        ssh_set_error(msg->sftp->session, SSH_FATAL,
985
0
                      "Invalid SSH_FXP_STATUS message");
986
0
        sftp_set_error(msg->sftp, SSH_FX_FAILURE);
987
0
        return NULL;
988
0
    }
989
990
0
    rc = ssh_buffer_unpack(msg->payload, "ss",
991
0
                           &status->errormsg,
992
0
                           &status->langmsg);
993
994
0
    if (rc != SSH_OK && msg->sftp->version >= 3) {
995
0
        SSH_LOG(SSH_LOG_WARN,
996
0
                "Invalid SSH_FXP_STATUS message. Missing error message.");
997
0
    }
998
999
0
    if (status->errormsg == NULL)
1000
0
        status->errormsg = strdup("No error message in packet");
1001
1002
0
    if (status->langmsg == NULL)
1003
0
        status->langmsg = strdup("");
1004
1005
0
    if (status->errormsg == NULL || status->langmsg == NULL) {
1006
0
        ssh_set_error_oom(msg->sftp->session);
1007
0
        sftp_set_error(msg->sftp, SSH_FX_FAILURE);
1008
0
        status_msg_free(status);
1009
0
        return NULL;
1010
0
    }
1011
1012
0
    return status;
1013
0
}
1014
1015
void status_msg_free(sftp_status_message status)
1016
0
{
1017
0
    if (status == NULL) {
1018
0
        return;
1019
0
    }
1020
1021
0
    SAFE_FREE(status->errormsg);
1022
0
    SAFE_FREE(status->langmsg);
1023
    SAFE_FREE(status);
1024
0
}
1025
1026
#endif /* WITH_SFTP */