/* Create a new channel, and start the open request. This is intended * for X11, agent, tcp forwarding, and should be filled with channel-specific * options, with the calling function calling encrypt_packet() after * completion. It is mandatory for the caller to encrypt_packet() if * DROPBEAR_SUCCESS is returned */ int send_msg_channel_open_init(int fd, const char * typestring) { struct Channel* chan; chan = newchannel(0, CHANNEL_ID_AGENT, 0, 0); if (!chan) { return DROPBEAR_FAILURE; } /* set fd non-blocking */ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { return DROPBEAR_FAILURE; } chan->infd = chan->outfd = fd; ses.maxfd = MAX(ses.maxfd, fd); /* now open the channel connection */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN); buf_putstring(ses.writepayload, typestring, strlen(typestring)); buf_putint(ses.writepayload, chan->index); buf_putint(ses.writepayload, RECV_MAXWINDOW); buf_putint(ses.writepayload, RECV_MAXPACKET); return DROPBEAR_SUCCESS; }
static void tcp_acceptor(struct Listener *listener, int sock) { int fd; struct sockaddr_storage addr; socklen_t len; char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); len = sizeof(addr); fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { m_close(fd); return; } if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { char* addr = NULL; unsigned int port = 0; if (tcpinfo->tcp_type == direct) { /* "direct-tcpip" */ /* host to connect, port to connect */ addr = tcpinfo->sendaddr; port = tcpinfo->sendport; } else { dropbear_assert(tcpinfo->tcp_type == forwarded); /* "forwarded-tcpip" */ /* address that was connected, port that was connected */ addr = tcpinfo->request_listenaddr; port = tcpinfo->listenport; } if (addr == NULL) { addr = "localhost"; } buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); /* originator ip */ buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); /* originator port */ buf_putint(ses.writepayload, atol(portstring)); encrypt_packet(); } else { /* XXX debug? */ close(fd); } }
/* Increment the incoming data window for a channel, and let the remote * end know */ static void send_msg_channel_window_adjust(struct Channel* channel, unsigned int incr) { TRACE(("sending window adjust %d", incr)); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST); buf_putint(ses.writepayload, channel->remotechan); buf_putint(ses.writepayload, incr); encrypt_packet(); }
/* Send a channel open failure message, with a corresponding reason * code (usually resource shortage or unknown chan type) */ static void send_msg_channel_open_failure(unsigned int remotechan, int reason, const unsigned char *text, const unsigned char *lang) { TRACE(("enter send_msg_channel_open_failure")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE); buf_putint(ses.writepayload, remotechan); buf_putint(ses.writepayload, reason); buf_putstring(ses.writepayload, text, strlen((char*)text)); buf_putstring(ses.writepayload, lang, strlen((char*)lang)); encrypt_packet(); TRACE(("leave send_msg_channel_open_failure")); }
/* Confirm a channel open, and let the remote end know what number we've * allocated and the receive parameters */ static void send_msg_channel_open_confirmation(struct Channel* channel, unsigned int recvwindow, unsigned int recvmaxpacket) { TRACE(("enter send_msg_channel_open_confirmation")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); buf_putint(ses.writepayload, channel->remotechan); buf_putint(ses.writepayload, channel->index); buf_putint(ses.writepayload, recvwindow); buf_putint(ses.writepayload, recvmaxpacket); encrypt_packet(); TRACE(("leave send_msg_channel_open_confirmation")); }
/* send the exitstatus to the client */ void send_msg_chansess_exitstatus(struct Channel * channel, struct ChanSess * chansess) { assert(chansess->exited); assert(chansess->exitsignal == -1); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); buf_putint(ses.writepayload, channel->remotechan); buf_putstring(ses.writepayload, "exit-status", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putint(ses.writepayload, chansess->exitstatus); encrypt_packet(); }
void send_gss_error(struct rekey_session *sess, gss_OID mech, int errmaj, int errmin) { mb_t msgbuf; msgbuf=buf_alloc(8); if (!msgbuf) return; buf_setlength(msgbuf, 8); do_gss_error(mech, errmaj, errmin, send_gss_error_cb, msgbuf); reset_cursor(msgbuf); if (buf_putint(msgbuf, ERR_AUTHN) || buf_putint(msgbuf, msgbuf->length - 8)) { prtmsg("internal error in send_gss_error, cannot pack message"); buf_free(msgbuf); return; } sess_send(sess, RESP_ERR, msgbuf); buf_free(msgbuf); }
/* Send a success message */ void send_msg_channel_success(struct Channel *channel) { TRACE(("enter send_msg_channel_success")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); TRACE(("leave send_msg_channel_success")); }
/* Send our list of algorithms we can use */ void send_msg_kexinit() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT); /* cookie */ genrandom(buf_getwriteptr(ses.writepayload, 16), 16); buf_incrwritepos(ses.writepayload, 16); /* kex algos */ buf_put_algolist(ses.writepayload, sshkex); /* server_host_key_algorithms */ buf_put_algolist(ses.writepayload, sshhostkey); /* encryption_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshciphers); /* encryption_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshciphers); /* mac_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshhashes); /* mac_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshhashes); /* compression_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshcompress); /* compression_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshcompress); /* languages_client_to_server */ buf_putstring(ses.writepayload, "", 0); /* languages_server_to_client */ buf_putstring(ses.writepayload, "", 0); /* first_kex_packet_follows - unimplemented for now */ buf_putbyte(ses.writepayload, 0x00); /* reserved unit32 */ buf_putint(ses.writepayload, 0); /* set up transmitted kex packet buffer for hashing. * This is freed after the end of the kex */ ses.transkexinit = buf_newcopy(ses.writepayload); encrypt_packet(); ses.dataallowed = 0; /* don't send other packets during kex */ ses.kexstate.sentkexinit = 1; }
/* This must be called directly after receiving the unimplemented packet. * Isn't the most clean implementation, it relies on packet processing * occurring directly after decryption. This is reasonably valid, since * there is only a single decryption buffer */ static void recv_unimplemented() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED); /* the decryption routine increments the sequence number, we must * decrement */ buf_putint(ses.writepayload, ses.recvseq - 1); encrypt_packet(); }
/* call this when trans/eof channels are closed */ static void send_msg_channel_eof(struct Channel *channel) { TRACE(("enter send_msg_channel_eof")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); /* we already know that trans/eof channels are closed */ send_msg_channel_close(channel); TRACE(("leave send_msg_channel_eof")); }
static void send_msg_global_request_remotetcp(const char *addr, int port) { TRACE(("enter send_msg_global_request_remotetcp")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); buf_putstring(ses.writepayload, "tcpip-forward", 13); buf_putbyte(ses.writepayload, 1); /* want_reply */ buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); encrypt_packet(); TRACE(("leave send_msg_global_request_remotetcp")) }
static void tcp_acceptor(struct Listener *listener, int sock) { int fd; struct sockaddr_storage addr; int len; char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); len = sizeof(addr); fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { return; } if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { buf_putstring(ses.writepayload, tcpinfo->sendaddr, strlen(tcpinfo->sendaddr)); buf_putint(ses.writepayload, tcpinfo->sendport); buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); buf_putint(ses.writepayload, atol(portstring)); encrypt_packet(); } else { /* XXX debug? */ close(fd); } }
static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { char* ipstring = NULL; if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) { ipstring = inet_ntoa(addr->sin_addr); buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); buf_putint(ses.writepayload, addr->sin_port); encrypt_packet(); return DROPBEAR_SUCCESS; } else { return DROPBEAR_FAILURE; } }
/* Send the close message and set the channel as closed */ static void send_msg_channel_close(struct Channel *channel) { TRACE(("enter send_msg_channel_close")); if (channel->type == CHANNEL_ID_SESSION) { send_exitsignalstatus(channel); } CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); channel->sentclosed = 1; TRACE(("leave send_msg_channel_close")); }
/* for our purposes we only need positive (or 0) numbers, so will * fail if we get negative numbers */ void buf_putmpint(buffer* buf, mp_int * mp) { unsigned int len, pad = 0; TRACE2(("enter buf_putmpint")) dropbear_assert(mp != NULL); if (SIGN(mp) == MP_NEG) { dropbear_exit("negative bignum"); } /* zero check */ if (USED(mp) == 1 && DIGIT(mp, 0) == 0) { len = 0; } else { /* SSH spec requires padding for mpints with the MSB set, this code * implements it */ len = mp_count_bits(mp); /* if the top bit of MSB is set, we need to pad */ pad = (len%8 == 0) ? 1 : 0; len = len / 8 + 1; /* don't worry about rounding, we need it for padding anyway when len%8 == 0 */ } /* store the length */ buf_putint(buf, len); /* store the actual value */ if (len > 0) { if (pad) { buf_putbyte(buf, 0x00); } if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) { dropbear_exit("mpint error"); } buf_incrwritepos(buf, len-pad); } TRACE2(("leave buf_putmpint")) }
/* send the signal causing the exit to the client */ void send_msg_chansess_exitsignal(struct Channel * channel, struct ChanSess * chansess) { int i; char* signame = NULL; assert(chansess->exited); assert(chansess->exitsignal > 0); CHECKCLEARTOWRITE(); /* we check that we can match a signal name, otherwise * don't send anything */ i = 0; while (signames[i].name != 0) { if (signames[i].signal == chansess->exitsignal) { signame = signames[i].name; break; } i++; } if (signame == NULL) { return; } buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); buf_putint(ses.writepayload, channel->remotechan); buf_putstring(ses.writepayload, "exit-signal", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putstring(ses.writepayload, signame, strlen(signame)); buf_putbyte(ses.writepayload, chansess->exitcore); buf_putstring(ses.writepayload, "", 0); /* error msg */ buf_putstring(ses.writepayload, "", 0); /* lang */ encrypt_packet(); }
/* encrypt the writepayload, putting into writebuf, ready for write_packet() * to put on the wire */ void encrypt_packet() { unsigned char padlen; unsigned char blocksize, macsize; buffer * writebuf; /* the packet which will go on the wire */ buffer * clearwritebuf; /* unencrypted, possibly compressed */ TRACE(("enter encrypt_packet()")); TRACE(("encrypt_packet type is %d", ses.writepayload->data[0])); blocksize = ses.keys->trans_algo_crypt->blocksize; macsize = ses.keys->trans_algo_mac->hashsize; /* Encrypted packet len is payload+5, then worst case is if we are 3 away * from a blocksize multiple. In which case we need to pad to the * multiple, then add another blocksize (or MIN_PACKET_LEN) */ clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3 #ifndef DISABLE_ZLIB + ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/ #endif ); buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(ses.writepayload, 0); #ifndef DISABLE_ZLIB /* compression */ if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) { buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); } else #endif { memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), buf_getptr(ses.writepayload, ses.writepayload->len), ses.writepayload->len); buf_incrwritepos(clearwritebuf, ses.writepayload->len); } /* finished with payload */ buf_setpos(ses.writepayload, 0); buf_setlen(ses.writepayload, 0); /* length of padding - packet length must be a multiple of blocksize, * with a minimum of 4 bytes of padding */ padlen = blocksize - (clearwritebuf->len) % blocksize; if (padlen < 4) { padlen += blocksize; } /* check for min packet length */ if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { padlen += blocksize; } buf_setpos(clearwritebuf, 0); /* packet length excluding the packetlength uint32 */ buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); /* padding len */ buf_putbyte(clearwritebuf, padlen); /* actual padding */ buf_setpos(clearwritebuf, clearwritebuf->len); buf_incrlen(clearwritebuf, padlen); genrandom(buf_getptr(clearwritebuf, padlen), padlen); /* do the actual encryption */ buf_setpos(clearwritebuf, 0); /* create a new writebuffer, this is freed when it has been put on the * wire by writepacket() */ writebuf = buf_new(clearwritebuf->len + macsize); if (ses.keys->trans_algo_crypt->cipherdesc == NULL) { /* copy it */ memcpy(buf_getwriteptr(writebuf, clearwritebuf->len), buf_getptr(clearwritebuf, clearwritebuf->len), clearwritebuf->len); buf_incrwritepos(writebuf, clearwritebuf->len); } else { /* encrypt it */ while (clearwritebuf->pos < clearwritebuf->len) { if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize), buf_getwriteptr(writebuf, blocksize), &ses.keys->trans_symmetric_struct) != CRYPT_OK) { dropbear_exit("error encrypting"); } buf_incrpos(clearwritebuf, blocksize); buf_incrwritepos(writebuf, blocksize); } } /* now add a hmac and we're done */ writemac(writebuf, clearwritebuf); /* clearwritebuf is finished with */ buf_free(clearwritebuf); /* enqueue the packet for sending */ buf_setpos(writebuf, 0); enqueue(&ses.writequeue, (void*)writebuf); /* Update counts */ ses.kexstate.datatrans += writebuf->len; ses.transseq++; TRACE(("leave encrypt_packet()")); }
/* Reads data from the server's program/shell/etc, and puts it in a * channel_data packet to send. * chan is the remote channel, isextended is 0 if it is normal data, 1 * if it is extended data. if it is extended, then the type is in * exttype */ static void send_msg_channel_data(struct Channel *channel, int isextended, unsigned int exttype) { buffer *buf; int len; unsigned int maxlen; int fd; TRACE(("enter send_msg_channel_data")); TRACE(("extended = %d type = %d", isextended, exttype)); CHECKCLEARTOWRITE(); assert(!channel->sentclosed); if (isextended) { if (channel->erreof) { TRACE(("leave send_msg_channel_data: erreof already set")); return; } assert(exttype == SSH_EXTENDED_DATA_STDERR); fd = channel->errfd; } else { if (channel->transeof) { TRACE(("leave send_msg_channel_data: transeof already set")); return; } fd = channel->outfd; } assert(fd >= 0); maxlen = MIN(channel->transwindow, channel->transmaxpacket); /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and * exttype if is extended */ maxlen = MIN(maxlen, ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); if (maxlen == 0) { TRACE(("leave send_msg_channel_data: no window")); return; /* the data will get written later */ } /* read the data */ buf = buf_new(maxlen); len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); if (len <= 0) { /* on error etc, send eof */ if (errno != EINTR) { if (isextended) { channel->erreof = 1; } else { channel->transeof = 1; } if ((channel->erreof || channel->errfd == -1) && channel->transeof) { send_msg_channel_eof(channel); } } buf_free(buf); TRACE(("leave send_msg_channel_data: len <= 0, erreof %d transeof %d", channel->erreof, channel->transeof)); return; } buf_incrlen(buf, len); buf_putbyte(ses.writepayload, isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); buf_putint(ses.writepayload, channel->remotechan); if (isextended) { buf_putint(ses.writepayload, exttype); } buf_putstring(ses.writepayload, buf_getptr(buf, len), len); buf_free(buf); channel->transwindow -= len; encrypt_packet(); TRACE(("leave send_msg_channel_data")); }
/* put a SSH style string into the buffer, increasing buffer len if required */ void buf_putstring(buffer* buf, const char* str, unsigned int len) { buf_putint(buf, len); buf_putbytes(buf, (const unsigned char*)str, len); }
void recv_msg_userauth_info_request() { unsigned char *name = NULL; unsigned char *instruction = NULL; unsigned int num_prompts = 0; unsigned int i; unsigned char *prompt = NULL; unsigned int echo = 0; unsigned char *response = NULL; TRACE(("enter recv_msg_recv_userauth_info_request")) /* Let the user know what password/host they are authing for */ if (!cli_ses.interact_request_received) { fprintf(stderr, "Login for %s@%s\n", cli_opts.username, //cli_opts.remotehost); cli_opts.remote_name_str); } cli_ses.interact_request_received = 1; name = buf_getstring(ses.payload, NULL); instruction = buf_getstring(ses.payload, NULL); /* language tag */ buf_eatstring(ses.payload); num_prompts = buf_getint(ses.payload); if (num_prompts >= DROPBEAR_MAX_CLI_INTERACT_PROMPTS) { dropbear_exit("Too many prompts received for keyboard-interactive"); } /* we'll build the response as we go */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_RESPONSE); buf_putint(ses.writepayload, num_prompts); if (strlen(name) > 0) { cleantext(name); fprintf(stderr, "%s", name); } m_free(name); if (strlen(instruction) > 0) { cleantext(instruction); fprintf(stderr, "%s", instruction); } m_free(instruction); for (i = 0; i < num_prompts; i++) { unsigned int response_len = 0; prompt = buf_getstring(ses.payload, NULL); cleantext(prompt); echo = buf_getbool(ses.payload); if (!echo) { unsigned char* p = getpass_or_cancel(prompt); response = m_strdup(p); m_burn(p, strlen(p)); } else { response = get_response(prompt); } response_len = strlen(response); buf_putstring(ses.writepayload, response, response_len); m_burn(response, response_len); m_free(response); } encrypt_packet(); TRACE(("leave recv_msg_recv_userauth_info_request")) }