~ubuntu-branches/ubuntu/utopic/dropbear/utopic-proposed

« back to all changes in this revision

Viewing changes to common-channel.c

  • Committer: Bazaar Package Importer
  • Author(s): Matt Johnston
  • Date: 2005-12-08 19:20:21 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051208192021-nyp9rwnt77nsg6ty
Tags: 0.47-1
* New upstream release.
* SECURITY: Fix incorrect buffer sizing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
52
52
static void checkinitdone(struct Channel *channel);
53
53
static void checkclose(struct Channel *channel);
54
54
 
55
 
static void closeinfd(struct Channel * channel);
56
 
static void closeoutfd(struct Channel * channel, int fd);
 
55
static void closewritefd(struct Channel * channel);
 
56
static void closereadfd(struct Channel * channel, int fd);
57
57
static void closechanfd(struct Channel *channel, int fd, int how);
58
58
 
59
59
#define FD_UNINIT (-2)
143
143
        newchan->transmaxpacket = transmaxpacket;
144
144
        
145
145
        newchan->typedata = NULL;
146
 
        newchan->infd = FD_UNINIT;
147
 
        newchan->outfd = FD_UNINIT;
 
146
        newchan->writefd = FD_UNINIT;
 
147
        newchan->readfd = FD_UNINIT;
148
148
        newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
149
149
        newchan->initconn = 0;
 
150
        newchan->await_open = 0;
150
151
 
151
152
        newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
152
153
        newchan->extrabuf = NULL; /* The user code can set it up */
162
163
        return newchan;
163
164
}
164
165
 
165
 
/* Get the channel structure corresponding to a channel number */
166
 
struct Channel* getchannel(unsigned int chan) {
 
166
/* Returns the channel structure corresponding to the channel in the current
 
167
 * data packet (ses.payload must be positioned appropriately) */
 
168
struct Channel* getchannel() {
 
169
 
 
170
        unsigned int chan;
 
171
 
 
172
        chan = buf_getint(ses.payload);
167
173
        if (chan >= ses.chansize || ses.channels[chan] == NULL) {
168
174
                return NULL;
169
175
        }
171
177
}
172
178
 
173
179
/* Iterate through the channels, performing IO if available */
174
 
void channelio(fd_set *readfd, fd_set *writefd) {
 
180
void channelio(fd_set *readfds, fd_set *writefds) {
175
181
 
176
182
        struct Channel *channel;
177
183
        unsigned int i;
186
192
                        continue;
187
193
                }
188
194
 
189
 
                /* read from program/pipe stdout */
190
 
                if (channel->outfd >= 0 && FD_ISSET(channel->outfd, readfd)) {
 
195
                /* read data and send it over the wire */
 
196
                if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
191
197
                        send_msg_channel_data(channel, 0, 0);
192
198
                }
193
199
 
194
 
                /* read from program/pipe stderr */
 
200
                /* read stderr data and send it over the wire */
195
201
                if (channel->extrabuf == NULL &&
196
 
                                channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
 
202
                                channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
197
203
                                send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
198
204
                }
199
205
 
200
 
                /* if we can read from the infd, it might be closed, so we try to
201
 
                 * see if it has errors */
202
 
                if (channel->infd >= 0 && channel->infd != channel->outfd
203
 
                                && FD_ISSET(channel->infd, readfd)) {
204
 
                        if (channel->initconn) {
205
 
                                /* Handling for "in progress" connection - this is needed
206
 
                                 * to avoid spinning 100% CPU when we connect to a server
207
 
                                 * which doesn't send anything (tcpfwding) */
208
 
                                checkinitdone(channel);
209
 
                                continue; /* Important not to use the channel after 
210
 
                                                         checkinitdone(), as it may be NULL */
211
 
                        }
212
 
                        ret = write(channel->infd, NULL, 0); /* Fake write */
213
 
                        if (ret < 0 && errno != EINTR && errno != EAGAIN) {
214
 
                                closeinfd(channel);
215
 
                        }
216
 
                }
217
 
 
218
206
                /* write to program/pipe stdin */
219
 
                if (channel->infd >= 0 && FD_ISSET(channel->infd, writefd)) {
 
207
                if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
220
208
                        if (channel->initconn) {
221
209
                                checkinitdone(channel);
222
210
                                continue; /* Important not to use the channel after
223
211
                                                         checkinitdone(), as it may be NULL */
224
212
                        }
225
 
                        writechannel(channel, channel->infd, channel->writebuf);
 
213
                        writechannel(channel, channel->writefd, channel->writebuf);
226
214
                }
227
215
                
228
216
                /* stderr for client mode */
229
217
                if (channel->extrabuf != NULL 
230
 
                                && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) {
 
218
                                && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
231
219
                        writechannel(channel, channel->errfd, channel->extrabuf);
232
220
                }
233
221
        
238
226
 
239
227
        /* Listeners such as TCP, X11, agent-auth */
240
228
#ifdef USING_LISTENERS
241
 
        handle_listeners(readfd);
 
229
        handle_listeners(readfds);
242
230
#endif
243
231
}
244
232
 
246
234
/* do all the EOF/close type stuff checking for a channel */
247
235
static void checkclose(struct Channel *channel) {
248
236
 
249
 
        TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d",
250
 
                                channel->infd, channel->outfd,
 
237
        TRACE(("checkclose: writefd %d, readfd %d, errfd %d, sentclosed %d, recvclosed %d",
 
238
                                channel->writefd, channel->readfd,
251
239
                                channel->errfd, channel->sentclosed, channel->recvclosed))
252
240
        TRACE(("writebuf %d extrabuf %s extrabuf %d",
253
241
                                cbuf_getused(channel->writebuf),
260
248
                 * if the shell has exited etc */
261
249
                if (channel->type->checkclose) {
262
250
                        if (channel->type->checkclose(channel)) {
263
 
                                closeinfd(channel);
 
251
                                closewritefd(channel);
264
252
                        }
265
253
                }
266
254
 
267
255
                if (!channel->senteof
268
 
                        && channel->outfd == FD_CLOSED 
 
256
                        && channel->readfd == FD_CLOSED 
269
257
                        && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
270
258
                        send_msg_channel_eof(channel);
271
259
                }
272
260
 
273
 
                if (channel->infd == FD_CLOSED
274
 
                        && channel->outfd == FD_CLOSED
 
261
                if (channel->writefd == FD_CLOSED
 
262
                        && channel->readfd == FD_CLOSED
275
263
                        && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
276
264
                        send_msg_channel_close(channel);
277
265
                }
308
296
 
309
297
        TRACE(("enter checkinitdone"))
310
298
 
311
 
        if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen)
 
299
        if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen)
312
300
                        || val != 0) {
313
301
                send_msg_channel_open_failure(channel->remotechan,
314
302
                                SSH_OPEN_CONNECT_FAILED, "", "");
315
 
                close(channel->infd);
 
303
                close(channel->writefd);
316
304
                deletechannel(channel);
317
305
                TRACE(("leave checkinitdone: fail"))
318
306
        } else {
319
307
                send_msg_channel_open_confirmation(channel, channel->recvwindow,
320
308
                                channel->recvmaxpacket);
321
 
                channel->outfd = channel->infd;
 
309
                channel->readfd = channel->writefd;
322
310
                channel->initconn = 0;
323
311
                TRACE(("leave checkinitdone: success"))
324
312
        }
380
368
                if (len < 0 && errno != EINTR) {
381
369
                        /* no more to write - we close it even if the fd was stderr, since
382
370
                         * that's a nasty failure too */
383
 
                        closeinfd(channel);
 
371
                        closewritefd(channel);
384
372
                }
385
373
                TRACE(("leave writechannel: len <= 0"))
386
374
                return;
389
377
        cbuf_incrread(cbuf, len);
390
378
        channel->recvdonelen += len;
391
379
 
392
 
        if (fd == channel->infd && len == maxlen && channel->recveof) { 
 
380
        if (fd == channel->writefd && len == maxlen && channel->recveof) { 
393
381
                /* Check if we're closing up */
394
 
                closeinfd(channel);
 
382
                closewritefd(channel);
395
383
                TRACE(("leave writechannel: recveof set"))
396
384
                return;
397
385
        }
404
392
                channel->recvdonelen = 0;
405
393
        }
406
394
 
407
 
        assert(channel->recvwindow <= RECV_MAXWINDOW);
408
 
        assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
409
 
        assert(channel->extrabuf == NULL ||
 
395
        dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
 
396
        dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
 
397
        dropbear_assert(channel->extrabuf == NULL ||
410
398
                        channel->recvwindow <= cbuf_getavail(channel->extrabuf));
411
399
        
412
400
        
415
403
 
416
404
/* Set the file descriptors for the main select in session.c
417
405
 * This avoid channels which don't have any window available, are closed, etc*/
418
 
void setchannelfds(fd_set *readfd, fd_set *writefd) {
 
406
void setchannelfds(fd_set *readfds, fd_set *writefds) {
419
407
        
420
408
        unsigned int i;
421
409
        struct Channel * channel;
430
418
                /* Stuff to put over the wire */
431
419
                if (channel->transwindow > 0) {
432
420
 
433
 
                        if (channel->outfd >= 0) {
434
 
                                FD_SET(channel->outfd, readfd);
 
421
                        if (channel->readfd >= 0) {
 
422
                                FD_SET(channel->readfd, readfds);
435
423
                        }
436
424
                        
437
425
                        if (channel->extrabuf == NULL && channel->errfd >= 0) {
438
 
                                        FD_SET(channel->errfd, readfd);
 
426
                                        FD_SET(channel->errfd, readfds);
439
427
                        }
440
428
                }
441
429
 
442
 
                /* For checking FD status (ie closure etc) - we don't actually
443
 
                 * read data from infd */
444
 
                TRACE(("infd = %d, outfd %d, errfd %d, bufused %d", 
445
 
                                        channel->infd, channel->outfd,
446
 
                                        channel->errfd,
447
 
                                        cbuf_getused(channel->writebuf) ))
448
 
                if (channel->infd >= 0 && channel->infd != channel->outfd) {
449
 
                        FD_SET(channel->infd, readfd);
450
 
                }
451
 
 
452
 
                /* Stuff from the wire, to local program/shell/user etc */
453
 
                if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 )
 
430
                /* Stuff from the wire */
 
431
                if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 )
454
432
                                || channel->initconn) {
455
433
 
456
 
                                FD_SET(channel->infd, writefd);
 
434
                                FD_SET(channel->writefd, writefds);
457
435
                }
458
436
 
459
437
                if (channel->extrabuf != NULL && channel->errfd >= 0 
460
438
                                && cbuf_getused(channel->extrabuf) > 0 ) {
461
 
                                FD_SET(channel->errfd, writefd);
 
439
                                FD_SET(channel->errfd, writefds);
462
440
                }
463
441
 
464
442
        } /* foreach channel */
465
443
 
466
444
#ifdef USING_LISTENERS
467
 
        set_listener_fds(readfd);
 
445
        set_listener_fds(readfds);
468
446
#endif
469
447
 
470
448
}
474
452
 * etc) FD is also EOF */
475
453
void recv_msg_channel_eof() {
476
454
 
477
 
        unsigned int chan;
478
455
        struct Channel * channel;
479
456
 
480
457
        TRACE(("enter recv_msg_channel_eof"))
481
458
 
482
 
        chan = buf_getint(ses.payload);
483
 
        channel = getchannel(chan);
484
 
 
 
459
        channel = getchannel();
485
460
        if (channel == NULL) {
486
461
                dropbear_exit("EOF for unknown channel");
487
462
        }
490
465
        if (cbuf_getused(channel->writebuf) == 0
491
466
                        && (channel->extrabuf == NULL 
492
467
                                        || cbuf_getused(channel->extrabuf) == 0)) {
493
 
                closeinfd(channel);
 
468
                closewritefd(channel);
494
469
        }
495
470
 
496
471
        TRACE(("leave recv_msg_channel_eof"))
500
475
/* Handle channel closure(), respond in kind and close the channels */
501
476
void recv_msg_channel_close() {
502
477
 
503
 
        unsigned int chan;
504
478
        struct Channel * channel;
505
479
 
506
480
        TRACE(("enter recv_msg_channel_close"))
507
481
 
508
 
        chan = buf_getint(ses.payload);
509
 
        TRACE(("close channel = %d", chan))
510
 
        channel = getchannel(chan);
511
 
 
 
482
        channel = getchannel();
512
483
        if (channel == NULL) {
513
484
                /* disconnect ? */
514
485
                dropbear_exit("Close for unknown channel");
542
513
 
543
514
        /* close the FDs in case they haven't been done
544
515
         * yet (ie they were shutdown etc */
545
 
        close(channel->infd);
546
 
        close(channel->outfd);
 
516
        close(channel->writefd);
 
517
        close(channel->readfd);
547
518
        close(channel->errfd);
548
519
 
549
520
        channel->typedata = NULL;
567
538
 * such as chansession or x11fwd */
568
539
void recv_msg_channel_request() {
569
540
 
570
 
        unsigned int chan;
571
541
        struct Channel *channel;
572
542
 
573
543
        TRACE(("enter recv_msg_channel_request"))
574
544
        
575
 
        chan = buf_getint(ses.payload);
576
 
        channel = getchannel(chan);
577
 
 
 
545
        channel = getchannel();
578
546
        if (channel == NULL) {
579
547
                /* disconnect ? */
580
548
                dropbear_exit("Unknown channel");
608
576
 
609
577
        CHECKCLEARTOWRITE();
610
578
 
611
 
        assert(!channel->sentclosed);
 
579
        dropbear_assert(!channel->sentclosed);
612
580
 
613
581
        if (isextended) {
614
582
                fd = channel->errfd;
615
583
        } else {
616
 
                fd = channel->outfd;
 
584
                fd = channel->readfd;
617
585
        }
618
 
        assert(fd >= 0);
 
586
        dropbear_assert(fd >= 0);
619
587
 
620
588
        maxlen = MIN(channel->transwindow, channel->transmaxpacket);
621
589
        /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and 
635
603
        if (len <= 0) {
636
604
                /* on error/eof, send eof */
637
605
                if (len == 0 || errno != EINTR) {
638
 
                        closeoutfd(channel, fd);
 
606
                        closereadfd(channel, fd);
639
607
                }
640
608
                buf_free(buf);
641
609
                buf = NULL;
666
634
/* We receive channel data */
667
635
void recv_msg_channel_data() {
668
636
 
669
 
        unsigned int chan;
670
637
        struct Channel *channel;
671
638
 
672
 
        chan = buf_getint(ses.payload);
673
 
        channel = getchannel(chan);
674
 
 
 
639
        channel = getchannel();
675
640
        if (channel == NULL) {
676
641
                dropbear_exit("Unknown channel");
677
642
        }
678
643
 
679
 
        common_recv_msg_channel_data(channel, channel->infd, channel->writebuf);
 
644
        common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf);
680
645
}
681
646
 
682
647
/* Shared for data and stderr data - when we receive data, put it in a buffer
696
661
        }
697
662
 
698
663
        if (fd < 0) {
699
 
                dropbear_exit("received data with bad infd");
 
664
                dropbear_exit("received data with bad writefd");
700
665
        }
701
666
 
702
667
        datalen = buf_getint(ses.payload);
726
691
                len -= buflen;
727
692
        }
728
693
 
729
 
        assert(channel->recvwindow >= datalen);
 
694
        dropbear_assert(channel->recvwindow >= datalen);
730
695
        channel->recvwindow -= datalen;
731
 
        assert(channel->recvwindow <= RECV_MAXWINDOW);
 
696
        dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
732
697
 
733
698
        TRACE(("leave recv_msg_channel_data"))
734
699
}
738
703
 * as data is sent, and incremented upon receiving window-adjust messages */
739
704
void recv_msg_channel_window_adjust() {
740
705
 
741
 
        unsigned int chan;
742
706
        struct Channel * channel;
743
707
        unsigned int incr;
744
708
        
745
 
        chan = buf_getint(ses.payload);
746
 
        channel = getchannel(chan);
747
 
 
 
709
        channel = getchannel();
748
710
        if (channel == NULL) {
749
711
                dropbear_exit("Unknown channel");
750
712
        }
941
903
        /* set fd non-blocking */
942
904
        setnonblocking(fd);
943
905
 
944
 
        chan->infd = chan->outfd = fd;
 
906
        chan->writefd = chan->readfd = fd;
945
907
        ses.maxfd = MAX(ses.maxfd, fd);
946
908
 
 
909
        chan->await_open = 1;
 
910
 
947
911
        /* now open the channel connection */
948
912
        CHECKCLEARTOWRITE();
949
913
 
961
925
 * successful*/
962
926
void recv_msg_channel_open_confirmation() {
963
927
 
964
 
        unsigned int chan;
965
928
        struct Channel * channel;
966
929
        int ret;
967
930
 
968
931
        TRACE(("enter recv_msg_channel_open_confirmation"))
969
 
        chan = buf_getint(ses.payload);
970
932
 
971
 
        channel = getchannel(chan);
 
933
        channel = getchannel();
972
934
        if (channel == NULL) {
973
935
                dropbear_exit("Unknown channel");
974
936
        }
975
937
 
 
938
        if (!channel->await_open) {
 
939
                dropbear_exit("unexpected channel reply");
 
940
        }
 
941
        channel->await_open = 0;
 
942
 
976
943
        channel->remotechan =  buf_getint(ses.payload);
977
944
        channel->transwindow = buf_getint(ses.payload);
978
945
        channel->transmaxpacket = buf_getint(ses.payload);
979
946
        
980
 
        TRACE(("new chan remote %d localho %d", channel->remotechan, chan))
 
947
        TRACE(("new chan remote %d local %d", 
 
948
                                channel->remotechan, channel->index))
981
949
 
982
950
        /* Run the inithandler callback */
983
951
        if (channel->type->inithandler) {
995
963
/* Notification that our channel open request failed */
996
964
void recv_msg_channel_open_failure() {
997
965
 
998
 
        unsigned int chan;
999
966
        struct Channel * channel;
1000
 
        chan = buf_getbyte(ses.payload);
1001
967
 
1002
 
        channel = getchannel(chan);
 
968
        channel = getchannel();
1003
969
        if (channel == NULL) {
1004
970
                dropbear_exit("Unknown channel");
1005
971
        }
1006
972
 
 
973
        if (!channel->await_open) {
 
974
                dropbear_exit("unexpected channel reply");
 
975
        }
 
976
        channel->await_open = 0;
 
977
 
1007
978
        removechannel(channel);
1008
979
}
1009
980
#endif /* USING_LISTENERS */
1010
981
 
1011
982
/* close a stdout/stderr fd */
1012
 
static void closeoutfd(struct Channel * channel, int fd) {
 
983
static void closereadfd(struct Channel * channel, int fd) {
1013
984
 
1014
 
        /* don't close it if it is the same as infd,
1015
 
         * unless infd is already set -1 */
1016
 
        TRACE(("enter closeoutfd"))
 
985
        /* don't close it if it is the same as writefd,
 
986
         * unless writefd is already set -1 */
 
987
        TRACE(("enter closereadfd"))
1017
988
        closechanfd(channel, fd, 0);
1018
 
        TRACE(("leave closeoutfd"))
 
989
        TRACE(("leave closereadfd"))
1019
990
}
1020
991
 
1021
992
/* close a stdin fd */
1022
 
static void closeinfd(struct Channel * channel) {
 
993
static void closewritefd(struct Channel * channel) {
1023
994
 
1024
 
        TRACE(("enter closeinfd"))
1025
 
        closechanfd(channel, channel->infd, 1);
1026
 
        TRACE(("leave closeinfd"))
 
995
        TRACE(("enter closewritefd"))
 
996
        closechanfd(channel, channel->writefd, 1);
 
997
        TRACE(("leave closewritefd"))
1027
998
}
1028
999
 
1029
1000
/* close a fd, how is 0 for stdout/stderr, 1 for stdin */
1033
1004
 
1034
1005
        /* XXX server */
1035
1006
        if (channel->type->sepfds) {
 
1007
                TRACE(("shutdown((%d), %d)", fd, how))
1036
1008
                shutdown(fd, how);
1037
1009
                if (how == 0) {
1038
1010
                        closeout = 1;
1044
1016
                closein = closeout = 1;
1045
1017
        }
1046
1018
 
1047
 
        if (closeout && fd == channel->outfd) {
1048
 
                channel->outfd = FD_CLOSED;
 
1019
        if (closeout && fd == channel->readfd) {
 
1020
                channel->readfd = FD_CLOSED;
1049
1021
        }
1050
1022
        if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
1051
1023
                channel->errfd = FD_CLOSED;
1052
1024
        }
1053
1025
 
1054
 
        if (closein && fd == channel->infd) {
1055
 
                channel->infd = FD_CLOSED;
 
1026
        if (closein && fd == channel->writefd) {
 
1027
                channel->writefd = FD_CLOSED;
1056
1028
        }
1057
1029
        if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
1058
1030
                channel->errfd = FD_CLOSED;
1059
1031
        }
 
1032
 
 
1033
        /* if we called shutdown on it and all references are gone, then we 
 
1034
         * need to close() it to stop it lingering */
 
1035
        if (channel->type->sepfds && channel->readfd == FD_CLOSED 
 
1036
                && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
 
1037
                close(fd);
 
1038
        }
1060
1039
}