/* 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; }
/* Send a signal to a session's process as requested by the client*/ static int sessionsignal(struct ChanSess *chansess) { int sig = 0; unsigned char* signame; int i; if (chansess->pid == 0) { /* haven't got a process pid yet */ return DROPBEAR_FAILURE; } signame = buf_getstring(ses.payload, NULL); i = 0; while (signames[i].name != 0) { if (strcmp(signames[i].name, signame) == 0) { sig = signames[i].signal; break; } i++; } m_free(signame); if (sig == 0) { /* failed */ return DROPBEAR_FAILURE; } if (kill(chansess->pid, sig) < 0) { return DROPBEAR_FAILURE; } return DROPBEAR_SUCCESS; }
/* Handle a command request from the client. This is used for both shell * and command-execution requests, and passes the command to * noptycommand or ptycommand as appropriate. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, char iscmd) { unsigned int cmdlen; TRACE(("enter sessioncommand")); if (chansess->cmd != NULL) { /* TODO - send error - multiple commands? */ return DROPBEAR_FAILURE; } if (iscmd) { /* "exec" */ chansess->cmd = buf_getstring(ses.payload, &cmdlen); if (cmdlen > MAX_CMD_LEN) { /* TODO - send error - too long ? */ return DROPBEAR_FAILURE; } } if (chansess->term == NULL) { /* no pty */ return noptycommand(channel, chansess); } else { /* want pty */ return ptycommand(channel, chansess); } }
/* Handle requests for a channel. These can be execution requests, * or x11/authagent forwarding. These are passed to appropriate handlers */ void chansessionrequest(struct Channel *channel) { unsigned char * type; unsigned int typelen; unsigned char wantreply; int ret = 1; struct ChanSess *chansess; TRACE(("enter chansessionrequest")); assert(channel->type == CHANNEL_ID_SESSION); type = buf_getstring(ses.payload, &typelen); wantreply = buf_getbyte(ses.payload); if (typelen > MAX_NAME_LEN) { send_msg_channel_failure(channel); m_free(type); TRACE(("leave chansessionrequest: type too long")); /* XXX send error?*/ return; } chansess = (struct ChanSess*)channel->typedata; assert(chansess != NULL); TRACE(("type is %s\n", type)); if (strcmp(type, "window-change") == 0) { ret = sessionwinchange(chansess); } else if (strcmp(type, "shell") == 0) { ret = sessioncommand(channel, chansess, 0); } else if (strcmp(type, "pty-req") == 0) { ret = sessionpty(chansess); } else if (strcmp(type, "exec") == 0) { ret = sessioncommand(channel, chansess, 1); #ifndef DISABLE_X11FWD } else if (strcmp(type, "x11-req") == 0) { ret = x11req(chansess); #endif #ifndef DISABLE_AGENTFWD } else if (strcmp(type, "*****@*****.**") == 0) { ret = agentreq(chansess); #endif } else if (strcmp(type, "signal") == 0) { ret = sessionsignal(chansess); } else { /* etc, todo "env", "subsystem" */ } if (wantreply) { if (ret == 0) { send_msg_channel_success(channel); } else { send_msg_channel_failure(channel); } } m_free(type); TRACE(("leave chansessionrequest")); }
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; }
/* Process a password auth request, sending success or failure messages as * appropriate */ void svr_auth_password() { char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt = NULL; /* crypt generated from the user's password sent */ unsigned char * password; unsigned int passwordlen; unsigned int changepw; passwdcrypt = ses.authstate.pw_passwd; #ifdef DEBUG_HACKCRYPT /* debugging crypt for non-root testing with shadows */ passwdcrypt = DEBUG_HACKCRYPT; #endif /* check if client wants to change password */ changepw = buf_getbool(ses.payload); if (changepw) { /* not implemented by this server */ send_msg_userauth_failure(0, 1); return; } password = buf_getstring(ses.payload, &passwordlen); /* the first bytes of passwdcrypt are the salt */ testcrypt = crypt((char*)password, passwdcrypt); m_burn(password, passwordlen); m_free(password); /* check for empty password */ if (passwdcrypt[0] == '\0') { dropbear_log(LOG_WARNING, "User '%s' has blank password, rejected", ses.authstate.pw_name); send_msg_userauth_failure(0, 1); return; } if (strcmp(testcrypt, passwdcrypt) == 0) { /* successful authentication */ dropbear_log(LOG_NOTICE, "Password auth succeeded for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, "Bad password attempt for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_failure(0, 1); } }
/* match the first algorithm in the comma-seperated list in buf which is * also in localalgos[], or return NULL on failure. */ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[]) { unsigned char * algolist = NULL; unsigned char * remotealgos[MAX_PROPOSED_ALGO]; unsigned int len; unsigned int count, i, j; algo_type * ret = NULL; /* get the comma-seperated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { goto out; /* just a sanity check, no other use */ } /* remotealgos will contain a list of the strings parsed out */ /* We will have at least one string (even if it's just "") */ remotealgos[0] = algolist; count = 1; /* Iterate through, replacing ','s with NULs, to split it into * words. */ for (i = 0; i < len; i++) { if (algolist[i] == '\0') { /* someone is trying something strange */ goto out; } if (algolist[i] == ',' && i != len) { algolist[i] = '\0'; remotealgos[count] = &algolist[i+1]; count++; } if (count == MAX_PROPOSED_ALGO) { break; } } /* iterate and find the first match */ for (i = 0; i < count; i++) { len = strlen(remotealgos[i]); for (j = 0; localalgos[j].name != NULL; j++) { if (!localalgos[j].usable) { continue; } if (len == strlen(localalgos[j].name) && strcmp(localalgos[j].name, remotealgos[i]) == 0) { ret = &localalgos[j]; goto out; } } } out: m_free(algolist); return ret; }
/* Return a string as a newly allocated buffer */ buffer * buf_getstringbuf(buffer *buf) { buffer *ret; unsigned char* str; unsigned int len; str = buf_getstring(buf, &len); ret = m_malloc(sizeof(*ret)); ret->data = str; ret->len = len; ret->size = len; ret->pos = 0; return ret; }
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) {
/* processes a SSH_MSG_SERVICE_REQUEST, returning 0 if finished, * 1 if not */ void recv_msg_service_request() { unsigned char * name; unsigned int len; TRACE(("enter recv_msg_service_request")) name = buf_getstring(ses.payload, &len); /* ssh-userauth */ if (len == SSH_SERVICE_USERAUTH_LEN && strncmp(SSH_SERVICE_USERAUTH, name, len) == 0) { send_msg_service_accept(name, len); m_free(name); TRACE(("leave recv_msg_service_request: done ssh-userauth")) return; }
/* This just sets up the state variables right for the main client session loop * to deal with */ void recv_msg_service_accept() { unsigned char* servicename; unsigned int len; TRACE(("enter recv_msg_service_accept")) servicename = buf_getstring(ses.payload, &len); /* ssh-userauth */ if (cli_ses.state == SERVICE_AUTH_REQ_SENT && len == SSH_SERVICE_USERAUTH_LEN && strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) { cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD; m_free(servicename); TRACE(("leave recv_msg_service_accept: done ssh-userauth")) return; }
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")) }
/* Process a password auth request, sending success or failure messages as * appropriate */ void svr_auth_password() { #ifdef HAVE_SHADOW_H struct spwd *spasswd = NULL; #endif char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt = NULL; /* crypt generated from the user's password sent */ unsigned char * password; unsigned int passwordlen; int passwd_cmp; unsigned int changepw; passwdcrypt = ses.authstate.pw_passwd; #ifdef HAVE_SHADOW_H /* get the shadow password if possible */ spasswd = getspnam(ses.authstate.pw_name); if (spasswd != NULL && spasswd->sp_pwdp != NULL) { passwdcrypt = spasswd->sp_pwdp; } #endif #ifdef DEBUG_HACKCRYPT /* debugging crypt for non-root testing with shadows */ passwdcrypt = DEBUG_HACKCRYPT; #endif /* check for empty password - need to do this again here * since the shadow password may differ to that tested * in auth.c */ if (passwdcrypt[0] == '\0' && !svr_opts.noauthpass && !(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) { dropbear_log(LOG_WARNING, "%s: User '%s' has blank password, rejected", __FUNCTION__, ses.authstate.pw_name); send_msg_userauth_failure(0, 1); return; } /* check if client wants to change password */ changepw = buf_getbool(ses.payload); if (changepw) { /* not implemented by this server */ send_msg_userauth_failure(0, 1); return; } password = buf_getstring(ses.payload, &passwordlen); /* the first bytes of passwdcrypt are the salt */ /* testcrypt = crypt((char*)password, passwdcrypt); passwd_cmp = strcmp(testcrypt, passwdcrypt); */ passwd_cmp = strcmp((char*)password, passwdcrypt); m_burn(password, passwordlen); m_free(password); if ( passwd_cmp == 0 ) { /* successful authentication */ dropbear_log(LOG_NOTICE, "Password auth succeeded for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, "Bad password attempt for '%s' from %s", ses.authstate.pw_name, svr_ses.addrstring); send_msg_userauth_failure(0, 1); } }
/* 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; }
/* Process a password auth request, sending success or failure messages as * appropriate */ void passwordauth() { #ifdef HAVE_SHADOW_H struct spwd *spasswd; #endif char * passwdcrypt; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt; /* crypt generated from the user's password sent */ unsigned char * password; unsigned int passwordlen; unsigned char changepw; passwdcrypt = ses.authstate.pw->pw_passwd; #ifdef HAVE_SHADOW_H /* get the shadow password if possible */ spasswd = getspnam(ses.authstate.pw->pw_name); if (spasswd != NULL && spasswd->sp_pwdp != NULL) { passwdcrypt = spasswd->sp_pwdp; } #endif #ifdef DEBUG_HACKCRYPT /* debugging crypt for non-root testing with shadows */ passwdcrypt = DEBUG_HACKCRYPT; #endif /* check for empty password - need to do this again here * since the shadow password may differ to that tested * in auth.c */ if (passwdcrypt[0] == '\0') { dropbear_log(LOG_WARNING, "disallowed login with empty password for '%s' from %s", ses.authstate.printableuser, ses.addrstring); send_msg_userauth_failure(0, 1); return; } /* check if client wants to change password */ changepw = buf_getbyte(ses.payload); if (changepw) { /* not implemented by this server */ send_msg_userauth_failure(0, 1); return; } password = buf_getstring(ses.payload, &passwordlen); /* clear the buffer containing the password */ buf_incrpos(ses.payload, -passwordlen - 4); m_burn(buf_getptr(ses.payload, passwordlen + 4), passwordlen + 4); /* the first bytes of passwdcrypt are the salt */ testcrypt = crypt((char*)password, passwdcrypt); if (strcmp(testcrypt, passwdcrypt) == 0) { /* successful authentication */ dropbear_log(LOG_NOTICE, "password auth succeeded for '%s' from %s", ses.authstate.printableuser, ses.addrstring); send_msg_userauth_success(); } else { dropbear_log(LOG_WARNING, "bad password attempt for '%s' from %s", ses.authstate.printableuser, ses.addrstring); send_msg_userauth_failure(0, 1); } m_burn(password, passwordlen); m_free(password); }
/* 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")); }
/* match the first algorithm in the comma-separated list in buf which is * also in localalgos[], or return NULL on failure. * (*goodguess) is set to 1 if the preferred client/server algos match, * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are * guessed correctly */ algo_type * buf_match_algo(buffer* buf, algo_type localalgos[], enum kexguess2_used *kexguess2, int *goodguess) { char * algolist = NULL; const char *remotenames[MAX_PROPOSED_ALGO], *localnames[MAX_PROPOSED_ALGO]; unsigned int len; unsigned int remotecount, localcount, clicount, servcount, i, j; algo_type * ret = NULL; const char **clinames, **servnames; if (goodguess) { *goodguess = 0; } /* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */ algolist = buf_getstring(buf, &len); TRACE(("buf_match_algo: %s", algolist)) if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { goto out; } /* remotenames will contain a list of the strings parsed out */ /* We will have at least one string (even if it's just "") */ remotenames[0] = algolist; remotecount = 1; for (i = 0; i < len; i++) { if (algolist[i] == '\0') { /* someone is trying something strange */ goto out; } if (algolist[i] == ',') { algolist[i] = '\0'; remotenames[remotecount] = &algolist[i+1]; remotecount++; } if (remotecount >= MAX_PROPOSED_ALGO) { break; } } if (kexguess2 && *kexguess2 == KEXGUESS2_LOOK) { for (i = 0; i < remotecount; i++) { if (strcmp(remotenames[i], KEXGUESS2_ALGO_NAME) == 0) { *kexguess2 = KEXGUESS2_YES; break; } } if (*kexguess2 == KEXGUESS2_LOOK) { *kexguess2 = KEXGUESS2_NO; } } for (i = 0; localalgos[i].name != NULL; i++) { if (localalgos[i].usable) { localnames[i] = localalgos[i].name; } else { localnames[i] = NULL; } } localcount = i; if (IS_DROPBEAR_SERVER) { clinames = remotenames; clicount = remotecount; servnames = localnames; servcount = localcount; } else { clinames = localnames; clicount = localcount; servnames = remotenames; servcount = remotecount; } /* iterate and find the first match */ for (i = 0; i < clicount; i++) { for (j = 0; j < servcount; j++) { if (!(servnames[j] && clinames[i])) { /* unusable algos are NULL */ continue; } if (strcmp(servnames[j], clinames[i]) == 0) { /* set if it was a good guess */ if (goodguess && kexguess2) { if (*kexguess2 == KEXGUESS2_YES) { if (i == 0) { *goodguess = 1; } } else { if (i == 0 && j == 0) { *goodguess = 1; } } } /* set the algo to return */ if (IS_DROPBEAR_SERVER) { ret = &localalgos[j]; } else { ret = &localalgos[i]; } goto out; } } } out: m_free(algolist); return ret; }
/* 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); }
/* Process a password auth request, sending success or failure messages as * appropriate */ void svr_auth_password() { //brcm begin #ifndef SSHD_GENKEY #ifdef HAVE_SHADOW_H struct spwd *spasswd = NULL; #endif char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */ char * testcrypt = NULL; /* crypt generated from the user's password sent */ unsigned char * password; unsigned int passwordlen; unsigned int changepw; // brcm add matched flag. int matched = 0; passwdcrypt = ses.authstate.pw->pw_passwd; #ifdef HAVE_SHADOW_H /* get the shadow password if possible */ spasswd = getspnam(ses.authstate.printableuser); if (spasswd != NULL && spasswd->sp_pwdp != NULL) { passwdcrypt = spasswd->sp_pwdp; } #endif #ifdef DEBUG_HACKCRYPT /* debugging crypt for non-root testing with shadows */ passwdcrypt = DEBUG_HACKCRYPT; #endif /* check for empty password - need to do this again here * since the shadow password may differ to that tested * in auth.c */ if (passwdcrypt[0] == '\0') { dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected", ses.authstate.printableuser); send_msg_userauth_failure(0, 1); return; } /* check if client wants to change password */ changepw = buf_getbool(ses.payload); if (changepw) { /* not implemented by this server */ send_msg_userauth_failure(0, 1); return; } password = buf_getstring(ses.payload, &passwordlen); /* the first bytes of passwdcrypt are the salt */ testcrypt = crypt((char*)password, passwdcrypt); // brcm add local/remote login check // We are doing all this auth checking inside sshd code instead of via proper CLI API. if ((glbAccessMode == NETWORK_ACCESS_LAN_SIDE && \ (!strcmp(ses.authstate.username, "user") || !strcmp(ses.authstate.username, "admin"))) || (glbAccessMode == NETWORK_ACCESS_WAN_SIDE && !strcmp(ses.authstate.username, "support"))) { matched = 1; strcpy(currUser, ses.authstate.username); if (!strcmp(currUser, "admin")) { currPerm = 0x80; /*PERM_ADMIN */ } else if (!strcmp(currUser, "support")) { currPerm = 0x40; /* PERM_SUPPORT */ } else if (!strcmp(currUser, "user")) { currPerm = 0x01; /* PERM_USER */ } } m_burn(password, passwordlen); m_free(password); if (strcmp(testcrypt, passwdcrypt) == 0 && matched) { /* successful authentication */ // brcm commented next msg //dropbear_log(LOG_NOTICE, // "password auth succeeded for '%s' from %s", // ses.authstate.printableuser, // svr_ses.addrstring); send_msg_userauth_success(); } else { #ifdef DESKTOP_LINUX dropbear_log(LOG_WARNING, "skip password auth for now, return success"); send_msg_userauth_success(); #else dropbear_log(LOG_WARNING, "bad password attempt for '%s' from %s", ses.authstate.printableuser, svr_ses.addrstring); send_msg_userauth_failure(0, 1); #endif } #endif // brcm end, ifndef SSHD_GENKEY }