/* Handle channel specific requests, passing off to corresponding handlers * such as chansession or x11fwd */ void recv_msg_channel_request() { unsigned int chan; struct Channel *channel; TRACE(("enter recv_msg_channel_request")); chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { /* disconnect ? */ dropbear_exit("Unknown channel"); } TRACE(("chan type is %d", channel->type)); /* handle according to channel type */ switch (channel->type) { case CHANNEL_ID_SESSION: TRACE(("continue recv_msg_channel_request: session request")); chansessionrequest(channel); break; default: send_msg_channel_failure(channel); } TRACE(("leave recv_msg_channel_request")); }
/* Retrieve an mp_int from the buffer. * Will fail for -ve since they shouldn't be required here. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int buf_getmpint(buffer* buf, mp_int* mp) { unsigned int len; len = buf_getint(buf); if (len == 0) { mp_zero(mp); return DROPBEAR_SUCCESS; } if (len > BUF_MAX_MPINT) { return DROPBEAR_FAILURE; } /* check for negative */ if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) { return DROPBEAR_FAILURE; } if (mp_read_unsigned_bin(mp, buf_getptr(buf, len), len) != MP_OKAY) { return DROPBEAR_FAILURE; } buf_incrpos(buf, len); return DROPBEAR_SUCCESS; }
/* handle the channel EOF event, by closing the channel filedescriptor. The * channel isn't closed yet, it is left until the incoming (from the program * etc) FD is also EOF */ void recv_msg_channel_eof() { unsigned int chan; struct Channel * channel; TRACE(("enter recv_msg_channel_eof")); chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { dropbear_exit("EOF for unknown channel"); } channel->recveof = 1; /* we should close the channel */ if (channel->type == CHANNEL_ID_X11 || channel->type == CHANNEL_ID_AGENT || channel->type == CHANNEL_ID_TCPDIRECT) { shutdown(channel->infd, 0); } else { close(channel->infd); } channel->infd = -1; if (channel->transeof && (channel->erreof || channel->errfd == -1) && !channel->sentclosed) { send_msg_channel_close(channel); } TRACE(("leave recv_msg_channel_eof")); }
/* when we receive channel data, put it in a buffer for writing to the program/ * shell etc */ void recv_msg_channel_data() { unsigned int chan; struct Channel * channel; unsigned int datalen; unsigned int pos; unsigned int maxdata; TRACE(("enter recv_msg_channel_data")); chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { /* disconnect ? */ dropbear_exit("Unknown channel"); } assert(channel->infd != -1); datalen = buf_getint(ses.payload); /* if the client is going to send us more data than we've allocated, then * it has ignored the windowsize, so we "MAY ignore all extra data" */ maxdata = channel->writebuf->size - channel->writebuf->pos; if (datalen > maxdata) { TRACE(("Warning: recv_msg_channel_data: extra data past window")); datalen = maxdata; } /* write to the buffer - we always append to the end of the buffer */ pos = channel->writebuf->pos; buf_setpos(channel->writebuf, channel->writebuf->len); memcpy(buf_getwriteptr(channel->writebuf, datalen), buf_getptr(ses.payload, datalen), datalen); buf_incrwritepos(channel->writebuf, datalen); buf_setpos(channel->writebuf, pos); /* revert pos */ channel->recvwindow -= datalen; /* if (channel->recvwindow < RECV_MINWINDOW) { send_msg_channel_window_adjust(channel, RECV_MAXWINDOW - channel->recvwindow); channel->recvwindow = RECV_MAXWINDOW; }*/ TRACE(("leave recv_msg_channel_data")); }
/* Let the process know that the window size has changed, as notified from the * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int sessionwinchange(struct ChanSess *chansess) { if (chansess->master < 0) { /* haven't got a pty yet */ return DROPBEAR_FAILURE; } chansess->termc = buf_getint(ses.payload); chansess->termr = buf_getint(ses.payload); chansess->termw = buf_getint(ses.payload); chansess->termh = buf_getint(ses.payload); pty_change_window_size(chansess->master, chansess->termr, chansess->termc, chansess->termw, chansess->termh); return DROPBEAR_FAILURE; }
static int newtcpforwarded(struct Channel * channel) { char *origaddr = NULL; unsigned int origport; m_list_elem * iter = NULL; struct TCPFwdEntry *fwd; char portstring[NI_MAXSERV]; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; origaddr = buf_getstring(ses.payload, NULL); origport = buf_getint(ses.payload); /* Find which port corresponds. First try and match address as well as port, in case they want to forward different ports separately ... */ for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { fwd = (struct TCPFwdEntry*)iter->item; if (origport == fwd->listenport && strcmp(origaddr, fwd->listenaddr) == 0) { break; } } if (!iter) { /* ... otherwise try to generically match the only forwarded port without address (also handles ::1 vs 127.0.0.1 vs localhost case). rfc4254 is vague about the definition of "address that was connected" */ for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { fwd = (struct TCPFwdEntry*)iter->item; if (origport == fwd->listenport) { break; } } } if (iter == NULL) { /* We didn't request forwarding on that port */ cleantext(origaddr); dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", origaddr, origport); goto out; } snprintf(portstring, sizeof(portstring), "%d", fwd->connectport); channel->conn_pending = connect_remote(AF_UNSPEC, fwd->connectaddr, portstring, channel_connect_done, channel); channel->prio = DROPBEAR_CHANNEL_PRIO_UNKNOWABLE; err = SSH_OPEN_IN_PROGRESS; out: m_free(origaddr); TRACE(("leave newtcpdirect: err %d", err)) return err; }
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int x11req(struct ChanSess * chansess) { int fd; if (!svr_pubkey_allows_x11fwd()) { return DROPBEAR_FAILURE; } /* we already have an x11 connection */ if (chansess->x11listener != NULL) { return DROPBEAR_FAILURE; } chansess->x11singleconn = buf_getbool(ses.payload); chansess->x11authprot = buf_getstring(ses.payload, NULL); chansess->x11authcookie = buf_getstring(ses.payload, NULL); chansess->x11screennum = buf_getint(ses.payload); /* create listening socket */ fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { goto fail; } /* allocate port and bind */ chansess->x11port = bindport(fd); if (chansess->x11port < 0) { goto fail; } /* listen */ if (listen(fd, 20) < 0) { goto fail; } /* set non-blocking */ setnonblocking(fd); /* listener code will handle the socket now. * No cleanup handler needed, since listener_remove only happens * from our cleanup anyway */ chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL); if (chansess->x11listener == NULL) { goto fail; } return DROPBEAR_SUCCESS; fail: /* cleanup */ m_free(chansess->x11authprot); m_free(chansess->x11authcookie); close(fd); return DROPBEAR_FAILURE; }
/* Confirmation that our channel open request (for forwardings) was * successful*/ void recv_msg_channel_open_confirmation() { unsigned int chan; struct Channel * channel; TRACE(("enter recv_msg_channel_open_confirmation")); chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { dropbear_exit("Unknown channel"); } channel->remotechan = buf_getint(ses.payload); channel->transwindow = buf_getint(ses.payload); channel->transmaxpacket = buf_getint(ses.payload); TRACE(("leave recv_msg_channel_open_confirmation")); }
/* Handle a diffie-hellman key exchange reply. */ void recv_msg_kexdh_reply() { DEF_MP_INT(dh_f); sign_key *hostkey = NULL; unsigned int type, keybloblen; unsigned char* keyblob = NULL; TRACE(("enter recv_msg_kexdh_reply")) if (cli_ses.kex_state != KEXDH_INIT_SENT) { dropbear_exit("Received out-of-order kexdhreply"); } m_mp_init(&dh_f); type = ses.newkeys->algo_hostkey; TRACE(("type is %d", type)) hostkey = new_sign_key(); keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); if (!ses.kexstate.donefirstkex) { /* Only makes sense the first time */ checkhostkey(keyblob, keybloblen); } if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { TRACE(("failed getting pubkey")) dropbear_exit("Bad KEX packet"); } if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) { TRACE(("failed getting mpint")) dropbear_exit("Bad KEX packet"); } kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey); mp_clear(&dh_f); mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL); m_free(cli_ses.dh_e); m_free(cli_ses.dh_x); if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) != DROPBEAR_SUCCESS) { dropbear_exit("Bad hostkey signature"); } sign_key_free(hostkey); hostkey = NULL; send_msg_newkeys(); ses.requirenext = SSH_MSG_NEWKEYS; TRACE(("leave recv_msg_kexdh_init")) }
/* Return a string as a newly allocated buffer */ buffer * buf_getstringbuf(buffer *buf) { buffer *ret = NULL; unsigned int len = buf_getint(buf); if (len > MAX_STRING_LEN) { dropbear_exit("String too long"); } ret = buf_new(len); memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len); buf_incrpos(buf, len); buf_incrlen(ret, len); return ret; }
/* Increment the outgoing data window for a channel - the remote end limits * the amount of data which may be transmitted, this window is decremented * as data is sent, and incremented upon receiving window-adjust messages */ void recv_msg_channel_window_adjust() { unsigned int chan; struct Channel * channel; unsigned int incr; chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { dropbear_exit("Unknown channel"); /* TODO - disconnect */ } incr = buf_getint(ses.payload); TRACE(("received window increment %d", incr)); incr = MIN(incr, MAX_TRANS_WIN_INCR); channel->transwindow += incr; channel->transwindow = MIN(channel->transwindow, MAX_TRANS_WINDOW); }
static int newtcpforwarded(struct Channel * channel) { char *origaddr = NULL; unsigned int origport; m_list_elem * iter = NULL; struct TCPFwdEntry *fwd; char portstring[NI_MAXSERV]; int sock; int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; origaddr = buf_getstring(ses.payload, NULL); origport = buf_getint(ses.payload); /* Find which port corresponds */ for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { fwd = (struct TCPFwdEntry*)iter->item; if (origport == fwd->listenport && (strcmp(origaddr, fwd->listenaddr) == 0)) { break; } } if (iter == NULL) { /* We didn't request forwarding on that port */ cleantext(origaddr); dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", origaddr, origport); goto out; } snprintf(portstring, sizeof(portstring), "%d", fwd->connectport); sock = connect_remote(AF_UNSPEC, fwd->connectaddr, portstring, 1, NULL); if (sock < 0) { TRACE(("leave newtcpdirect: sock failed")) err = SSH_OPEN_CONNECT_FAILED; goto out; } ses.maxfd = MAX(ses.maxfd, sock); /* We don't set readfd, that will get set after the connection's * progress succeeds */ channel->writefd = sock; channel->initconn = 1; err = SSH_OPEN_IN_PROGRESS; out: m_free(origaddr); TRACE(("leave newtcpdirect: err %d", err)) return err; }
static void cli_chansessreq(struct Channel *channel) { unsigned char* type = NULL; int wantreply; TRACE(("enter cli_chansessreq")) type = buf_getstring(ses.payload, NULL); wantreply = buf_getbool(ses.payload); if (strcmp(type, "exit-status") == 0) { cli_ses.retval = buf_getint(ses.payload); TRACE(("got exit-status of '%d'", cli_ses.retval)) } else if (strcmp(type, "exit-signal") == 0) {
/* Return a null-terminated string, it is malloced, so must be free()ed * Note that the string isn't checked for null bytes, hence the retlen * may be longer than what is returned by strlen */ char* buf_getstring(buffer* buf, unsigned int *retlen) { unsigned int len; char* ret; len = buf_getint(buf); if (len > MAX_STRING_LEN) { dropbear_exit("String too long"); } if (retlen != NULL) { *retlen = len; } ret = m_malloc(len+1); memcpy(ret, buf_getptr(buf, len), len); buf_incrpos(buf, len); ret[len] = '\0'; return ret; }
/* The only global success/failure messages are for remotetcp. * Since there isn't any identifier in these messages, we have to rely on them * being in the same order as we sent the requests. This is the ordering * of the cli_opts.remotefwds list. * If the requested remote port is 0 the listen port will be * dynamically allocated by the server and the port number will be returned * to client and the port number reported to the user. */ void cli_recv_msg_request_success() { /* We just mark off that we have received the reply, * so that we can report failure for later ones. */ m_list_elem * iter = NULL; for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; if (!fwd->have_reply) { fwd->have_reply = 1; if (fwd->listenport == 0) { /* The server should let us know which port was allocated if we requestd port 0 */ int allocport = buf_getint(ses.payload); if (allocport > 0) { dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d", allocport, fwd->connectaddr, fwd->connectport); } } return; } } }
/* Handle channel closure(), respond in kind and close the channels */ void recv_msg_channel_close() { unsigned int chan; struct Channel * channel; TRACE(("enter recv_msg_channel_close")); chan = buf_getint(ses.payload); TRACE(("close channel = %d", chan)); channel = getchannel(chan); if (channel == NULL) { /* disconnect ? */ dropbear_exit("Close for unknown channel"); } if (!channel->sentclosed) { send_msg_channel_close(channel); } closechannel(channel); TRACE(("leave recv_msg_channel_close")); }
/* Function used to read the initial portion of a packet, and determine the * length. Only called during the first BLOCKSIZE of a packet. */ static void read_packet_init() { unsigned int maxlen; int len; unsigned char blocksize; unsigned char macsize; blocksize = ses.keys->recv_algo_crypt->blocksize; macsize = ses.keys->recv_algo_mac->hashsize; if (ses.readbuf == NULL) { /* start of a new packet */ ses.readbuf = buf_new(INIT_READBUF); assert(ses.decryptreadbuf == NULL); ses.decryptreadbuf = buf_new(blocksize); } maxlen = blocksize - ses.readbuf->pos; /* read the rest of the packet if possible */ len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), maxlen); if (len == 0) { dropbear_close("remote host closed connection"); } if (len < 0) { if (errno == EINTR) { TRACE(("leave read_packet_init: EINTR")); return; } dropbear_exit("error reading"); } buf_incrwritepos(ses.readbuf, len); if ((unsigned int)len != maxlen) { /* don't have enough bytes to determine length, get next time */ return; } /* now we have the first block, need to get packet length, so we decrypt * the first block (only need first 4 bytes) */ buf_setpos(ses.readbuf, 0); if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { /* copy it */ memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize), buf_getptr(ses.readbuf, blocksize), blocksize); } else { /* decrypt it */ if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), buf_getwriteptr(ses.decryptreadbuf,blocksize), &ses.keys->recv_symmetric_struct) != CRYPT_OK) { dropbear_exit("error decrypting"); } } buf_setlen(ses.decryptreadbuf, blocksize); len = buf_getint(ses.decryptreadbuf) + 4 + macsize; buf_setpos(ses.readbuf, blocksize); /* check packet length */ if ((len > MAX_PACKET_LEN) || (len < MIN_PACKET_LEN + macsize) || ((len - macsize) % blocksize != 0)) { dropbear_exit("bad packet size"); } buf_resize(ses.readbuf, len); buf_setlen(ses.readbuf, len); }
/* read the client's choice of algorithms */ static void read_kex() { algo_type * algo; unsigned char* str; char * erralgo = NULL; buf_incrpos(ses.payload, 16); /* start after the cookie */ ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); /* kex_algorithms */ algo = buf_match_algo(ses.payload, sshkex); if (algo == NULL) { erralgo = "kex"; goto error; } ses.newkeys->algo_kex = algo->val; /* server_host_key_algorithms */ algo = buf_match_algo(ses.payload, sshhostkey); if (algo == NULL) { erralgo = "hostkey"; goto error; } ses.newkeys->algo_hostkey = algo->val; /* encryption_algorithms_client_to_server */ algo = buf_match_algo(ses.payload, sshciphers); if (algo == NULL) { erralgo = "enc c->s"; goto error; } ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data; /* encryption_algorithms_server_to_client */ algo = buf_match_algo(ses.payload, sshciphers); if (algo == NULL) { erralgo = "enc s->c"; goto error; } ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data; /* mac_algorithms_client_to_server */ algo = buf_match_algo(ses.payload, sshhashes); if (algo == NULL) { erralgo = "mac c->s"; goto error; } ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data; /* mac_algorithms_server_to_client */ algo = buf_match_algo(ses.payload, sshhashes); if (algo == NULL) { erralgo = "mac s->c"; goto error; } ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data; /* compression_algorithms_client_to_server */ algo = buf_match_algo(ses.payload, sshcompress); if (algo == NULL) { erralgo = "comp c->s"; goto error; } ses.newkeys->recv_algo_comp = algo->val; /* compression_algorithms_server_to_client */ algo = buf_match_algo(ses.payload, sshcompress); if (algo == NULL) { erralgo = "comp s->c"; goto error; } ses.newkeys->trans_algo_comp = algo->val; /* languages_client_to_server */ str = buf_getstring(ses.payload, NULL); m_free(str); /* languages_server_to_client */ str = buf_getstring(ses.payload, NULL); m_free(str); /* first_kex_packet_follows */ if (buf_getbyte(ses.payload)) { ses.kexstate.firstfollows = 1; /* XXX currently not handled */ } /* reserved for future extensions */ buf_getint(ses.payload); return; error: dropbear_exit("no matching algo %s", erralgo); }
/* Handle a new channel request, performing any channel-type-specific setup */ void recv_msg_channel_open() { unsigned char* type; unsigned int typelen; unsigned int typeval; unsigned int remotechan, transwindow, transmaxpacket; struct Channel* channel; unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE; TRACE(("enter recv_msg_channel_open")); /* get the packet contents */ type = buf_getstring(ses.payload, &typelen); remotechan = buf_getint(ses.payload); transwindow = buf_getint(ses.payload); transwindow = MIN(transwindow, MAX_TRANS_WINDOW); transmaxpacket = buf_getint(ses.payload); transmaxpacket = MIN(transmaxpacket, MAX_TRANS_PAYLOAD_LEN); /* figure what type of packet it is */ if (typelen > MAX_NAME_LEN) { goto failure; } if (strcmp(type, "session") == 0) { typeval = CHANNEL_ID_SESSION; #ifndef DISABLE_LOCALTCPFWD } else if (strcmp(type, "direct-tcpip") == 0) { typeval = CHANNEL_ID_TCPDIRECT; #endif } else { goto failure; } /* create the channel */ channel = newchannel(remotechan, typeval, transwindow, transmaxpacket); if (channel == NULL) { errtype = SSH_OPEN_RESOURCE_SHORTAGE; goto failure; } /* type specific initialisation */ if (typeval == CHANNEL_ID_SESSION) { newchansess(channel); #ifndef DISABLE_LOCALTCPFWD } else if (typeval == CHANNEL_ID_TCPDIRECT) { if (newtcpdirect(channel) == DROPBEAR_FAILURE) { deletechannel(channel); goto failure; } #endif } /* success */ send_msg_channel_open_confirmation(channel, channel->recvwindow, channel->recvmaxpacket); goto cleanup; failure: send_msg_channel_open_failure(remotechan, errtype, "", ""); cleanup: m_free(type); TRACE(("leave recv_msg_channel_open")); }
/* Just increment the buffer position the same as if we'd used buf_getstring, * but don't bother copying/malloc()ing for it */ void buf_eatstring(buffer *buf) { buf_incrpos( buf, buf_getint(buf) ); }
/* Handle a diffie-hellman key exchange reply. */ void recv_msg_kexdh_reply() { sign_key *hostkey = NULL; unsigned int type, keybloblen; unsigned char* keyblob = NULL; TRACE(("enter recv_msg_kexdh_reply")) if (cli_ses.kex_state != KEXDH_INIT_SENT) { dropbear_exit("Received out-of-order kexdhreply"); } type = ses.newkeys->algo_hostkey; TRACE(("type is %d", type)) hostkey = new_sign_key(); keybloblen = buf_getint(ses.payload); keyblob = buf_getptr(ses.payload, keybloblen); if (!ses.kexstate.donefirstkex) { /* Only makes sense the first time */ checkhostkey(keyblob, keybloblen); } if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) { TRACE(("failed getting pubkey")) dropbear_exit("Bad KEX packet"); } switch (ses.newkeys->algo_kex->mode) { case DROPBEAR_KEX_NORMAL_DH: { DEF_MP_INT(dh_f); m_mp_init(&dh_f); if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) { TRACE(("failed getting mpint")) dropbear_exit("Bad KEX packet"); } kexdh_comb_key(cli_ses.dh_param, &dh_f, hostkey); mp_clear(&dh_f); } break; case DROPBEAR_KEX_ECDH: #ifdef DROPBEAR_ECDH { buffer *ecdh_qs = buf_getstringbuf(ses.payload); kexecdh_comb_key(cli_ses.ecdh_param, ecdh_qs, hostkey); buf_free(ecdh_qs); } #endif break; #ifdef DROPBEAR_CURVE25519 case DROPBEAR_KEX_CURVE25519: { buffer *ecdh_qs = buf_getstringbuf(ses.payload); kexcurve25519_comb_key(cli_ses.curve25519_param, ecdh_qs, hostkey); buf_free(ecdh_qs); } #endif break; } if (cli_ses.dh_param) { free_kexdh_param(cli_ses.dh_param); cli_ses.dh_param = NULL; } #ifdef DROPBEAR_ECDH if (cli_ses.ecdh_param) { free_kexecdh_param(cli_ses.ecdh_param); cli_ses.ecdh_param = NULL; } #endif #ifdef DROPBEAR_CURVE25519 if (cli_ses.curve25519_param) { free_kexcurve25519_param(cli_ses.curve25519_param); cli_ses.curve25519_param = NULL; } #endif cli_ses.param_kex_algo = NULL; if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) { dropbear_exit("Bad hostkey signature"); } sign_key_free(hostkey); hostkey = NULL; send_msg_newkeys(); ses.requirenext[0] = SSH_MSG_NEWKEYS; ses.requirenext[1] = 0; TRACE(("leave recv_msg_kexdh_init")) }
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")) }
/* Set up a session pty which will be used to execute the shell or program. * The pty is allocated now, and kept for when the shell/program executes. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int sessionpty(struct ChanSess * chansess) { unsigned int termlen; unsigned char namebuf[65]; struct termios termio; TRACE(("enter sessionpty")); chansess->term = buf_getstring(ses.payload, &termlen); if (termlen > MAX_TERM_LEN) { /* TODO send disconnect ? */ TRACE(("leave sessionpty: term len too long")); return DROPBEAR_FAILURE; } chansess->termc = buf_getint(ses.payload); chansess->termr = buf_getint(ses.payload); chansess->termw = buf_getint(ses.payload); chansess->termh = buf_getint(ses.payload); /* allocate the pty */ assert(chansess->master == -1); /* haven't already got one */ if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { TRACE(("leave sessionpty: failed to allocate pty")); return DROPBEAR_FAILURE; } chansess->tty = (char*)strdup(namebuf); if (!chansess->tty) { dropbear_exit("out of memory"); /* TODO disconnect */ } pty_setowner(ses.authstate.pw, chansess->tty); pty_change_window_size(chansess->master, chansess->termr, chansess->termc, chansess->termw, chansess->termh); /* Term modes */ /* We'll ignore errors and continue if we can't set modes. * We're ignoring baud rates since they seem evil */ if (tcgetattr(chansess->master, &termio) == 0) { unsigned char opcode; unsigned int value; const struct TermCode * termcode; while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) { /* handle types of code */ if (opcode > MAX_TERMCODE) { continue; } termcode = &termcodes[(unsigned int)opcode]; value = buf_getint(ses.payload); switch (termcode->type) { case TERMCODE_NONE: break; case TERMCODE_CONTROLCHAR: termio.c_cc[termcode->mapcode] = value; break; case TERMCODE_INPUT: if (value) { termio.c_iflag |= termcode->mapcode; } else { termio.c_iflag &= ~(termcode->mapcode); } break; case TERMCODE_OUTPUT: if (value) { termio.c_oflag |= termcode->mapcode; } else { termio.c_oflag &= ~(termcode->mapcode); } break; case TERMCODE_LOCAL: if (value) { termio.c_lflag |= termcode->mapcode; } else { termio.c_lflag &= ~(termcode->mapcode); } break; case TERMCODE_CONTROL: if (value) { termio.c_cflag |= termcode->mapcode; } else { termio.c_cflag &= ~(termcode->mapcode); } break; } } if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { dropbear_log(LOG_INFO, "error setting terminal attributes"); } } TRACE(("leave sessionpty")); return DROPBEAR_SUCCESS; }