~ubuntu-branches/ubuntu/lucid/openssh/lucid

« back to all changes in this revision

Viewing changes to sftp-server.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2008-09-30 23:09:58 UTC
  • mfrom: (1.13.3 upstream) (29 hardy)
  • mto: This revision was merged to the branch mainline in revision 43.
  • Revision ID: james.westby@ubuntu.com-20080930230958-o6vsgn8c4mm959s0
Tags: 1:5.1p1-3
* Remove unnecessary ssh-vulnkey output in non-verbose mode when no
  compromised or unknown keys were found (closes: #496495).
* Configure with --disable-strip; dh_strip will deal with stripping
  binaries and will honour DEB_BUILD_OPTIONS (thanks, Bernhard R. Link;
  closes: #498681).
* Fix handling of zero-length server banners (thanks, Tomas Mraz; closes:
  #497026).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $OpenBSD: sftp-server.c,v 1.71 2007/01/03 07:22:36 stevesk Exp $ */
 
1
/* $OpenBSD: sftp-server.c,v 1.84 2008/06/26 06:10:09 djm Exp $ */
2
2
/*
3
3
 * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
4
4
 *
23
23
#ifdef HAVE_SYS_TIME_H
24
24
# include <sys/time.h>
25
25
#endif
 
26
#ifdef HAVE_SYS_MOUNT_H
 
27
#include <sys/mount.h>
 
28
#endif
 
29
#ifdef HAVE_SYS_STATVFS_H
 
30
#include <sys/statvfs.h>
 
31
#endif
26
32
 
27
33
#include <dirent.h>
28
34
#include <errno.h>
98
104
        case EINVAL:
99
105
                ret = SSH2_FX_BAD_MESSAGE;
100
106
                break;
 
107
        case ENOSYS:
 
108
                ret = SSH2_FX_OP_UNSUPPORTED;
 
109
                break;
101
110
        default:
102
111
                ret = SSH2_FX_FAILURE;
103
112
                break;
169
178
        int fd;
170
179
        char *name;
171
180
        u_int64_t bytes_read, bytes_write;
 
181
        int next_unused;
172
182
};
173
183
 
174
184
enum {
177
187
        HANDLE_FILE
178
188
};
179
189
 
180
 
Handle  handles[100];
 
190
Handle *handles = NULL;
 
191
u_int num_handles = 0;
 
192
int first_unused_handle = -1;
181
193
 
182
 
static void
183
 
handle_init(void)
 
194
static void handle_unused(int i)
184
195
{
185
 
        u_int i;
186
 
 
187
 
        for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
188
 
                handles[i].use = HANDLE_UNUSED;
 
196
        handles[i].use = HANDLE_UNUSED;
 
197
        handles[i].next_unused = first_unused_handle;
 
198
        first_unused_handle = i;
189
199
}
190
200
 
191
201
static int
192
202
handle_new(int use, const char *name, int fd, DIR *dirp)
193
203
{
194
 
        u_int i;
 
204
        int i;
195
205
 
196
 
        for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
197
 
                if (handles[i].use == HANDLE_UNUSED) {
198
 
                        handles[i].use = use;
199
 
                        handles[i].dirp = dirp;
200
 
                        handles[i].fd = fd;
201
 
                        handles[i].name = xstrdup(name);
202
 
                        handles[i].bytes_read = handles[i].bytes_write = 0;
203
 
                        return i;
204
 
                }
 
206
        if (first_unused_handle == -1) {
 
207
                if (num_handles + 1 <= num_handles)
 
208
                        return -1;
 
209
                num_handles++;
 
210
                handles = xrealloc(handles, num_handles, sizeof(Handle));
 
211
                handle_unused(num_handles - 1);
205
212
        }
206
 
        return -1;
 
213
 
 
214
        i = first_unused_handle;
 
215
        first_unused_handle = handles[i].next_unused;
 
216
 
 
217
        handles[i].use = use;
 
218
        handles[i].dirp = dirp;
 
219
        handles[i].fd = fd;
 
220
        handles[i].name = xstrdup(name);
 
221
        handles[i].bytes_read = handles[i].bytes_write = 0;
 
222
 
 
223
        return i;
207
224
}
208
225
 
209
226
static int
210
227
handle_is_ok(int i, int type)
211
228
{
212
 
        return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
213
 
            handles[i].use == type;
 
229
        return i >= 0 && (u_int)i < num_handles && handles[i].use == type;
214
230
}
215
231
 
216
232
static int
300
316
 
301
317
        if (handle_is_ok(handle, HANDLE_FILE)) {
302
318
                ret = close(handles[handle].fd);
303
 
                handles[handle].use = HANDLE_UNUSED;
304
319
                xfree(handles[handle].name);
 
320
                handle_unused(handle);
305
321
        } else if (handle_is_ok(handle, HANDLE_DIR)) {
306
322
                ret = closedir(handles[handle].dirp);
307
 
                handles[handle].use = HANDLE_UNUSED;
308
323
                xfree(handles[handle].name);
 
324
                handle_unused(handle);
309
325
        } else {
310
326
                errno = ENOENT;
311
327
        }
319
335
                logit("%s%sclose \"%s\" bytes read %llu written %llu",
320
336
                    emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
321
337
                    handle_to_name(handle),
322
 
                    handle_bytes_read(handle), handle_bytes_write(handle));
 
338
                    (unsigned long long)handle_bytes_read(handle),
 
339
                    (unsigned long long)handle_bytes_write(handle));
323
340
        } else {
324
341
                logit("%s%sclosedir \"%s\"",
325
342
                    emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
332
349
{
333
350
        u_int i;
334
351
 
335
 
        for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
 
352
        for (i = 0; i < num_handles; i++)
336
353
                if (handles[i].use != HANDLE_UNUSED)
337
354
                        handle_log_close(i, "forced");
338
355
}
467
484
        buffer_free(&msg);
468
485
}
469
486
 
 
487
static void
 
488
send_statvfs(u_int32_t id, struct statvfs *st)
 
489
{
 
490
        Buffer msg;
 
491
        u_int64_t flag;
 
492
 
 
493
        flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0;
 
494
        flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0;
 
495
 
 
496
        buffer_init(&msg);
 
497
        buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY);
 
498
        buffer_put_int(&msg, id);
 
499
        buffer_put_int64(&msg, st->f_bsize);
 
500
        buffer_put_int64(&msg, st->f_frsize);
 
501
        buffer_put_int64(&msg, st->f_blocks);
 
502
        buffer_put_int64(&msg, st->f_bfree);
 
503
        buffer_put_int64(&msg, st->f_bavail);
 
504
        buffer_put_int64(&msg, st->f_files);
 
505
        buffer_put_int64(&msg, st->f_ffree);
 
506
        buffer_put_int64(&msg, st->f_favail);
 
507
        buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid));
 
508
        buffer_put_int64(&msg, flag);
 
509
        buffer_put_int64(&msg, st->f_namemax);
 
510
        send_msg(&msg);
 
511
        buffer_free(&msg);
 
512
}
 
513
 
470
514
/* parse incoming */
471
515
 
472
516
static void
479
523
        buffer_init(&msg);
480
524
        buffer_put_char(&msg, SSH2_FXP_VERSION);
481
525
        buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
 
526
        /* POSIX rename extension */
 
527
        buffer_put_cstring(&msg, "posix-rename@openssh.com");
 
528
        buffer_put_cstring(&msg, "1"); /* version */
 
529
        /* statvfs extension */
 
530
        buffer_put_cstring(&msg, "statvfs@openssh.com");
 
531
        buffer_put_cstring(&msg, "2"); /* version */
 
532
        /* fstatvfs extension */
 
533
        buffer_put_cstring(&msg, "fstatvfs@openssh.com");
 
534
        buffer_put_cstring(&msg, "2"); /* version */
482
535
        send_msg(&msg);
483
536
        buffer_free(&msg);
484
537
}
702
755
        a = get_attrib();
703
756
        debug("request %u: setstat name \"%s\"", id, name);
704
757
        if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
705
 
                logit("set \"%s\" size %llu", name, a->size);
 
758
                logit("set \"%s\" size %llu",
 
759
                    name, (unsigned long long)a->size);
706
760
                ret = truncate(name, a->size);
707
761
                if (ret == -1)
708
762
                        status = errno_to_portable(errno);
709
763
        }
710
764
        if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
711
765
                logit("set \"%s\" mode %04o", name, a->perm);
712
 
                ret = chmod(name, a->perm & 0777);
 
766
                ret = chmod(name, a->perm & 07777);
713
767
                if (ret == -1)
714
768
                        status = errno_to_portable(errno);
715
769
        }
754
808
                char *name = handle_to_name(handle);
755
809
 
756
810
                if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
757
 
                        logit("set \"%s\" size %llu", name, a->size);
 
811
                        logit("set \"%s\" size %llu",
 
812
                            name, (unsigned long long)a->size);
758
813
                        ret = ftruncate(fd, a->size);
759
814
                        if (ret == -1)
760
815
                                status = errno_to_portable(errno);
762
817
                if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
763
818
                        logit("set \"%s\" mode %04o", name, a->perm);
764
819
#ifdef HAVE_FCHMOD
765
 
                        ret = fchmod(fd, a->perm & 0777);
 
820
                        ret = fchmod(fd, a->perm & 07777);
766
821
#else
767
 
                        ret = chmod(name, a->perm & 0777);
 
822
                        ret = chmod(name, a->perm & 07777);
768
823
#endif
769
824
                        if (ret == -1)
770
825
                                status = errno_to_portable(errno);
915
970
        name = get_string(NULL);
916
971
        a = get_attrib();
917
972
        mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
918
 
            a->perm & 0777 : 0777;
 
973
            a->perm & 07777 : 0777;
919
974
        debug3("request %u: mkdir", id);
920
975
        logit("mkdir name \"%s\" mode 0%o", name, mode);
921
976
        ret = mkdir(name, mode);
987
1042
                /* Race-free rename of regular files */
988
1043
                if (link(oldpath, newpath) == -1) {
989
1044
                        if (errno == EOPNOTSUPP
 
1045
#ifdef EXDEV
 
1046
                            || errno == EXDEV
 
1047
#endif
990
1048
#ifdef LINK_OPNOTSUPP_ERRNO
991
1049
                            || errno == LINK_OPNOTSUPP_ERRNO
992
1050
#endif
1070
1128
}
1071
1129
 
1072
1130
static void
 
1131
process_extended_posix_rename(u_int32_t id)
 
1132
{
 
1133
        char *oldpath, *newpath;
 
1134
 
 
1135
        oldpath = get_string(NULL);
 
1136
        newpath = get_string(NULL);
 
1137
        debug3("request %u: posix-rename", id);
 
1138
        logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
 
1139
        if (rename(oldpath, newpath) == -1)
 
1140
                send_status(id, errno_to_portable(errno));
 
1141
        else
 
1142
                send_status(id, SSH2_FX_OK);
 
1143
        xfree(oldpath);
 
1144
        xfree(newpath);
 
1145
}
 
1146
 
 
1147
static void
 
1148
process_extended_statvfs(u_int32_t id)
 
1149
{
 
1150
        char *path;
 
1151
        struct statvfs st;
 
1152
 
 
1153
        path = get_string(NULL);
 
1154
        debug3("request %u: statfs", id);
 
1155
        logit("statfs \"%s\"", path);
 
1156
 
 
1157
        if (statvfs(path, &st) != 0)
 
1158
                send_status(id, errno_to_portable(errno));
 
1159
        else
 
1160
                send_statvfs(id, &st);
 
1161
        xfree(path);
 
1162
}
 
1163
 
 
1164
static void
 
1165
process_extended_fstatvfs(u_int32_t id)
 
1166
{
 
1167
        int handle, fd;
 
1168
        struct statvfs st;
 
1169
 
 
1170
        handle = get_handle();
 
1171
        debug("request %u: fstatvfs \"%s\" (handle %u)",
 
1172
            id, handle_to_name(handle), handle);
 
1173
        if ((fd = handle_to_fd(handle)) < 0) {
 
1174
                send_status(id, SSH2_FX_FAILURE);
 
1175
                return;
 
1176
        }
 
1177
        if (fstatvfs(fd, &st) != 0)
 
1178
                send_status(id, errno_to_portable(errno));
 
1179
        else
 
1180
                send_statvfs(id, &st);
 
1181
}
 
1182
 
 
1183
static void
1073
1184
process_extended(void)
1074
1185
{
1075
1186
        u_int32_t id;
1077
1188
 
1078
1189
        id = get_int();
1079
1190
        request = get_string(NULL);
1080
 
        send_status(id, SSH2_FX_OP_UNSUPPORTED);                /* MUST */
 
1191
        if (strcmp(request, "posix-rename@openssh.com") == 0)
 
1192
                process_extended_posix_rename(id);
 
1193
        else if (strcmp(request, "statvfs@openssh.com") == 0)
 
1194
                process_extended_statvfs(id);
 
1195
        else if (strcmp(request, "fstatvfs@openssh.com") == 0)
 
1196
                process_extended_fstatvfs(id);
 
1197
        else
 
1198
                send_status(id, SSH2_FX_OP_UNSUPPORTED);        /* MUST */
1081
1199
        xfree(request);
1082
1200
}
1083
1201
 
1100
1218
        if (msg_len > SFTP_MAX_MSG_LENGTH) {
1101
1219
                error("bad message from %s local user %s",
1102
1220
                    client_addr, pw->pw_name);
1103
 
                cleanup_exit(11);
 
1221
                sftp_server_cleanup_exit(11);
1104
1222
        }
1105
1223
        if (buf_len < msg_len + 4)
1106
1224
                return;
1173
1291
                break;
1174
1292
        }
1175
1293
        /* discard the remaining bytes from the current packet */
1176
 
        if (buf_len < buffer_len(&iqueue))
1177
 
                fatal("iqueue grew unexpectedly");
 
1294
        if (buf_len < buffer_len(&iqueue)) {
 
1295
                error("iqueue grew unexpectedly");
 
1296
                sftp_server_cleanup_exit(255);
 
1297
        }
1178
1298
        consumed = buf_len - buffer_len(&iqueue);
1179
 
        if (msg_len < consumed)
1180
 
                fatal("msg_len %d < consumed %d", msg_len, consumed);
 
1299
        if (msg_len < consumed) {
 
1300
                error("msg_len %d < consumed %d", msg_len, consumed);
 
1301
                sftp_server_cleanup_exit(255);
 
1302
        }
1181
1303
        if (msg_len > consumed)
1182
1304
                buffer_consume(&iqueue, msg_len - consumed);
1183
1305
}
1184
1306
 
1185
1307
/* Cleanup handler that logs active handles upon normal exit */
1186
1308
void
1187
 
cleanup_exit(int i)
 
1309
sftp_server_cleanup_exit(int i)
1188
1310
{
1189
1311
        if (pw != NULL && client_addr != NULL) {
1190
1312
                handle_log_exit();
1195
1317
}
1196
1318
 
1197
1319
static void
1198
 
usage(void)
 
1320
sftp_server_usage(void)
1199
1321
{
1200
1322
        extern char *__progname;
1201
1323
 
1205
1327
}
1206
1328
 
1207
1329
int
1208
 
main(int argc, char **argv)
 
1330
sftp_server_main(int argc, char **argv, struct passwd *user_pw)
1209
1331
{
1210
1332
        fd_set *rset, *wset;
1211
1333
        int in, out, max, ch, skipargs = 0, log_stderr = 0;
1212
1334
        ssize_t len, olen, set_size;
1213
1335
        SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1214
 
        char *cp;
 
1336
        char *cp, buf[4*4096];
1215
1337
 
1216
1338
        extern char *optarg;
1217
1339
        extern char *__progname;
1218
1340
 
1219
 
        /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1220
 
        sanitise_stdfd();
1221
 
 
1222
1341
        __progname = ssh_get_progname(argv[0]);
1223
1342
        log_init(__progname, log_level, log_facility, log_stderr);
1224
1343
 
1241
1360
                        break;
1242
1361
                case 'f':
1243
1362
                        log_facility = log_facility_number(optarg);
1244
 
                        if (log_level == SYSLOG_FACILITY_NOT_SET)
 
1363
                        if (log_facility == SYSLOG_FACILITY_NOT_SET)
1245
1364
                                error("Invalid log facility \"%s\"", optarg);
1246
1365
                        break;
1247
1366
                case 'h':
1248
1367
                default:
1249
 
                        usage();
 
1368
                        sftp_server_usage();
1250
1369
                }
1251
1370
        }
1252
1371
 
1254
1373
 
1255
1374
        if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1256
1375
                client_addr = xstrdup(cp);
1257
 
                if ((cp = strchr(client_addr, ' ')) == NULL)
1258
 
                        fatal("Malformed SSH_CONNECTION variable: \"%s\"",
 
1376
                if ((cp = strchr(client_addr, ' ')) == NULL) {
 
1377
                        error("Malformed SSH_CONNECTION variable: \"%s\"",
1259
1378
                            getenv("SSH_CONNECTION"));
 
1379
                        sftp_server_cleanup_exit(255);
 
1380
                }
1260
1381
                *cp = '\0';
1261
1382
        } else
1262
1383
                client_addr = xstrdup("UNKNOWN");
1263
1384
 
1264
 
        if ((pw = getpwuid(getuid())) == NULL)
1265
 
                fatal("No user found for uid %lu", (u_long)getuid());
1266
 
        pw = pwcopy(pw);
 
1385
        pw = pwcopy(user_pw);
1267
1386
 
1268
1387
        logit("session opened for local user %s from [%s]",
1269
1388
            pw->pw_name, client_addr);
1270
1389
 
1271
 
        handle_init();
1272
 
 
1273
1390
        in = dup(STDIN_FILENO);
1274
1391
        out = dup(STDOUT_FILENO);
1275
1392
 
1295
1412
                memset(rset, 0, set_size);
1296
1413
                memset(wset, 0, set_size);
1297
1414
 
1298
 
                FD_SET(in, rset);
 
1415
                /*
 
1416
                 * Ensure that we can read a full buffer and handle
 
1417
                 * the worst-case length packet it can generate,
 
1418
                 * otherwise apply backpressure by stopping reads.
 
1419
                 */
 
1420
                if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
 
1421
                    buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
 
1422
                        FD_SET(in, rset);
 
1423
 
1299
1424
                olen = buffer_len(&oqueue);
1300
1425
                if (olen > 0)
1301
1426
                        FD_SET(out, wset);
1304
1429
                        if (errno == EINTR)
1305
1430
                                continue;
1306
1431
                        error("select: %s", strerror(errno));
1307
 
                        cleanup_exit(2);
 
1432
                        sftp_server_cleanup_exit(2);
1308
1433
                }
1309
1434
 
1310
1435
                /* copy stdin to iqueue */
1311
1436
                if (FD_ISSET(in, rset)) {
1312
 
                        char buf[4*4096];
1313
1437
                        len = read(in, buf, sizeof buf);
1314
1438
                        if (len == 0) {
1315
1439
                                debug("read eof");
1316
 
                                cleanup_exit(0);
 
1440
                                sftp_server_cleanup_exit(0);
1317
1441
                        } else if (len < 0) {
1318
1442
                                error("read: %s", strerror(errno));
1319
 
                                cleanup_exit(1);
 
1443
                                sftp_server_cleanup_exit(1);
1320
1444
                        } else {
1321
1445
                                buffer_append(&iqueue, buf, len);
1322
1446
                        }
1326
1450
                        len = write(out, buffer_ptr(&oqueue), olen);
1327
1451
                        if (len < 0) {
1328
1452
                                error("write: %s", strerror(errno));
1329
 
                                cleanup_exit(1);
 
1453
                                sftp_server_cleanup_exit(1);
1330
1454
                        } else {
1331
1455
                                buffer_consume(&oqueue, len);
1332
1456
                        }
1333
1457
                }
1334
 
                /* process requests from client */
1335
 
                process();
 
1458
 
 
1459
                /*
 
1460
                 * Process requests from client if we can fit the results
 
1461
                 * into the output buffer, otherwise stop processing input
 
1462
                 * and let the output queue drain.
 
1463
                 */
 
1464
                if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
 
1465
                        process();
1336
1466
        }
1337
1467
}