static int server_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; char *ctype = 0; int r; u_int rchan, rmaxpack, rwindow; size_t len; if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || (r = sshpkt_get_u32(ssh, &rchan)) != 0 || (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) goto out; debug("server_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "session") == 0) { c = server_request_session(ssh); } else if (strcmp(ctype, "direct-tcpip") == 0) { c = server_request_direct_tcpip(ssh); } else if (strcmp(ctype, "*****@*****.**") == 0) { c = server_request_direct_streamlocal(ssh); } else if (strcmp(ctype, "*****@*****.**") == 0) { c = server_request_tun(ssh); } if (c != NULL) { debug("server_input_channel_open: confirm %s", ctype); c->remote_id = rchan; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_put_u32(ssh, c->self)) != 0 || (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0|| (r = sshpkt_send(ssh)) != 0) goto out; } } else { debug("server_input_channel_open: failure %s", ctype); if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, rchan)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || (!(ssh->compat & SSH_BUG_OPENFAILURE) && (r = sshpkt_put_cstring(ssh, "open failed")) != 0) || (!(ssh->compat & SSH_BUG_OPENFAILURE) && (r = sshpkt_put_cstring(ssh, "")) != 0) || (r = sshpkt_send(ssh)) != 0) goto out; } r = 0; out: free(ctype); return r; }
static Channel * server_request_direct_tcpip(struct ssh *ssh) { Channel *c = NULL; char *target, *originator; u_int target_port, originator_port; int r; if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &target_port)) != 0 || (r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); debug("server_request_direct_tcpip: originator %s port %d, target %s " "port %d", originator, originator_port, target, target_port); /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag) { c = channel_connect_to_port(ssh, target, target_port, "direct-tcpip", "direct-tcpip"); } else { logit("refused local port forward: " "originator %s port %d, target %s port %d", originator, originator_port, target, target_port); } free(originator); free(target); return c; }
static int input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; int r; u_int min = 0, max = 0, nbits = 0; debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); if ((r = sshpkt_get_u32(ssh, &min)) != 0 || (r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_u32(ssh, &max)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; kex->min = min; kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); if (kex->max < kex->min || kex->nbits < kex->min || kex->max < kex->nbits) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } /* Contact privileged parent */ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); if (kex->dh == NULL) { sshpkt_disconnect(ssh, "no matching DH grp found"); r = SSH_ERR_ALLOC_FAIL; goto out; } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; /* Compute our exchange value in parallel with the client */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) goto out; debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; out: return r; }
static int server_input_window_size(int type, u_int32_t seq, struct ssh *ssh) { u_int row, col, xpixel, ypixel; int r; if ((r = sshpkt_get_u32(ssh, &row)) != 0 || (r = sshpkt_get_u32(ssh, &col)) != 0 || (r = sshpkt_get_u32(ssh, &xpixel)) != 0 || (r = sshpkt_get_u32(ssh, &ypixel)) != 0 || (r = sshpkt_get_end(ssh))) return r; debug("Window change received."); if (fdin != -1) pty_change_window_size(fdin, row, col, xpixel, ypixel); return 0; }
u_int ssh_packet_get_int(struct ssh *ssh) { u_int val; int r; if ((r = sshpkt_get_u32(ssh, &val)) != 0) fatal("%s: %s", __func__, ssh_err(r)); return val; }
static Channel * server_request_tun(struct ssh *ssh) { Channel *c = NULL; int r, sock; u_int tun, mode; if ((r = sshpkt_get_u32(ssh, &mode)) != 0 || (r = sshpkt_get_u32(ssh, &tun)) != 0) fatal("%s: %s", __func__, ssh_err(r)); switch (mode) { case SSH_TUNMODE_POINTOPOINT: case SSH_TUNMODE_ETHERNET: break; default: ssh_packet_send_debug(ssh, "Unsupported tunnel device mode."); return NULL; } if ((options.permit_tun & mode) == 0) { ssh_packet_send_debug(ssh, "Server has rejected tunnel device " "forwarding"); return NULL; } if (forced_tun_device >= 0) { if (tun != SSH_TUNID_ANY && (u_int)forced_tun_device != tun) goto done; tun = forced_tun_device; } sock = tun_open(tun, mode); if (sock < 0) goto done; c = channel_new(ssh, "tun", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; done: if (c == NULL) ssh_packet_send_debug(ssh, "Failed to open the tunnel device."); return c; }
/* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; const u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) if ((r = sshpkt_get_u8(ssh, NULL)) != 0) return r; for (i = 0; i < PROPOSAL_MAX; i++) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; /* * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported * KEX method has the server move first, but a server might be using * a custom method or one that we otherwise don't support. We should * be prepared to remember first_kex_follows here so we can eat a * packet later. * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means * for cases where the server *doesn't* go first. I guess we should * ignore it when it is set for these cases, which is what we do now. */ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); return SSH_ERR_INTERNAL_ERROR; }
/* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) if ((r = sshpkt_get_u8(ssh, NULL)) != 0) return r; for (i = 0; i < PROPOSAL_MAX; i++) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || (r = sshpkt_get_u32(ssh, NULL)) != 0 || (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); return SSH_ERR_INTERNAL_ERROR; }
static int server_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) { Channel *c; int r, success = 0; u_int id; u_char reply; char *rtype = NULL; if ((r = sshpkt_get_u32(ssh, &id)) != 0 || (r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &reply)) != 0) goto out; debug("server_input_channel_req: channel %u request %s reply %d", id, rtype, reply); if ((c = channel_lookup(id)) == NULL) ssh_packet_disconnect(ssh, "server_input_channel_req: " "unknown channel %u", id); if (!strcmp(rtype, "*****@*****.**")) { if ((r = sshpkt_get_end(ssh)) != 0) goto out; chan_rcvd_eow(c); } else if ((c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) success = session_input_channel_req(c, rtype); if (reply && !(c->flags & CHAN_CLOSE_SENT)) { if ((r = sshpkt_start(ssh, success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; } r = 0; out: if (rtype) free(rtype); return r; }
int kex_input_ext_info(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; u_int32_t i, ninfo; char *name, *val, *found; int r; debug("SSH2_MSG_EXT_INFO received"); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { free(name); return r; } debug("%s: %s=<%s>", __func__, name, val); if (strcmp(name, "server-sig-algs") == 0) { found = match_list("rsa-sha2-256", val, NULL); if (found) { kex->rsa_sha2 = 256; free(found); } found = match_list("rsa-sha2-512", val, NULL); if (found) { kex->rsa_sha2 = 512; free(found); } } free(name); free(val); } return sshpkt_get_end(ssh); }
/* * We only support those mechanisms that we know about (ie ones that we know * how to check local user kuserok and the like) */ static int userauth_gssapi(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; gss_OID_desc goid = {0, NULL}; Gssctxt *ctxt = NULL; int r, present; u_int mechs; OM_uint32 ms; size_t len; u_char *doid = NULL; if ((r = sshpkt_get_u32(ssh, &mechs)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (mechs == 0) { debug("Mechanism negotiation is not supported"); return (0); } do { mechs--; free(doid); present = 0; if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && doid[1] == len - 2) { goid.elements = doid + 2; goid.length = len - 2; ssh_gssapi_test_oid_supported(&ms, &goid, &present); } else { logit("Badly formed OID received"); } } while (mechs > 0 && !present); if (!present) { free(doid); authctxt->server_caused_failure = 1; return (0); } if (!authctxt->valid || authctxt->user == NULL) { debug2("%s: disabled because of invalid user", __func__); free(doid); return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { if (ctxt != NULL) ssh_gssapi_delete_ctx(&ctxt); free(doid); authctxt->server_caused_failure = 1; return (0); } authctxt->methoddata = (void *)ctxt; /* Return the OID that we received */ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 || (r = sshpkt_put_string(ssh, doid, len)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); free(doid); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); authctxt->postponed = 1; return (0); }
static int input_userauth_info_response(int type, u_int32_t seq, struct ssh *ssh) { struct authctxt *authctxt = ssh->authctxt; struct kbdintctxt *kbdintctxt; int authenticated = 0, res; int r; u_int i, nresp; const char *devicename = NULL; char **response = NULL; if (authctxt == NULL) fatal("input_userauth_info_response: no authctxt"); kbdintctxt = authctxt->kbdintctxt; if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) fatal("input_userauth_info_response: no kbdintctxt"); if (kbdintctxt->device == NULL) fatal("input_userauth_info_response: no device"); authctxt->postponed = 0; /* reset */ if ((r = sshpkt_get_u32(ssh, &nresp)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (nresp != kbdintctxt->nreq) fatal("input_userauth_info_response: wrong number of replies"); if (nresp > 100) fatal("input_userauth_info_response: too many replies"); if (nresp > 0) { response = xcalloc(nresp, sizeof(char *)); for (i = 0; i < nresp; i++) if ((r = sshpkt_get_cstring(ssh, &response[i], NULL)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if ((r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response); for (i = 0; i < nresp; i++) { explicit_bzero(response[i], strlen(response[i])); free(response[i]); } free(response); switch (res) { case 0: /* Success! */ authenticated = authctxt->valid ? 1 : 0; break; case 1: /* Authentication needs further interaction */ if (send_userauth_info_request(ssh) == 1) authctxt->postponed = 1; break; default: /* Failure! */ break; } devicename = kbdintctxt->device->name; if (!authctxt->postponed) { if (authenticated) { auth2_challenge_stop(ssh); } else { /* start next device */ /* may set authctxt->postponed */ auth2_challenge_start(ssh); } } userauth_finish(ssh, authenticated, "keyboard-interactive", devicename); return 0; }
static int server_input_global_request(int type, u_int32_t seq, struct ssh *ssh) { char *rtype = NULL; u_char want_reply; int r, success = 0, allocated_listen_port = 0; struct sshbuf *resp = NULL; if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || (r = sshpkt_get_u8(ssh, &want_reply)) != 0) goto out; debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { struct authctxt *authctxt = ssh->authctxt; struct Forward fwd; if (authctxt->pw == NULL || !authctxt->valid) fatal("server_input_global_request: no/invalid user"); memset(&fwd, 0, sizeof(fwd)); if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &fwd.listen_port)) != 0) goto out; debug("server_input_global_request: tcpip-forward listen %s port %d", fwd.listen_host, fwd.listen_port); /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || (!want_reply && fwd.listen_port == 0) || (fwd.listen_port != 0 && fwd.listen_port < IPPORT_RESERVED && authctxt->pw->pw_uid != 0)) { success = 0; ssh_packet_send_debug(ssh, "Server has disabled port forwarding."); } else { /* Start listening on the port */ success = channel_setup_remote_fwd_listener(ssh, &fwd, &allocated_listen_port, &options.fwd_opts); } free(fwd.listen_host); if ((resp = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); if (allocated_listen_port != 0 && (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r)); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); if ((r = sshpkt_get_cstring(ssh, &fwd.listen_host, NULL)) != 0 || (r = sshpkt_get_u32(ssh, &fwd.listen_port)) != 0) goto out; debug("%s: cancel-tcpip-forward addr %s port %d", __func__, fwd.listen_host, fwd.listen_port); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_host); } else if (strcmp(rtype, "*****@*****.**") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) goto out; debug("server_input_global_request: streamlocal-forward listen path %s", fwd.listen_path); /* check permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag) { success = 0; ssh_packet_send_debug(ssh, "Server has disabled port forwarding."); } else { /* Start listening on the socket */ success = channel_setup_remote_fwd_listener(ssh, &fwd, NULL, &options.fwd_opts); } free(fwd.listen_path); } else if (strcmp(rtype, "*****@*****.**") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); if ((r = sshpkt_get_cstring(ssh, &fwd.listen_path, NULL)) != 0) goto out; debug("%s: cancel-streamlocal-forward path %s", __func__, fwd.listen_path); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_path); } else if (strcmp(rtype, "*****@*****.**") == 0) { no_more_sessions = 1; success = 1; } else if (strcmp(rtype, "*****@*****.**") == 0) { success = server_input_hostkeys_prove(&resp); } if (want_reply) { if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE)) != 0 || (success && resp != NULL && (r = sshpkt_put(ssh, sshbuf_ptr(resp), sshbuf_len(resp))) != 0) || (r = sshpkt_send(ssh)) != 0) goto out; ssh_packet_write_wait(ssh); } r = 0; out: free(rtype); sshbuf_free(resp); return r; }
static int input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh) { Kex *kex = ssh->kex; int r, min = -1, max = -1, nbits = -1; switch (type) { case SSH2_MSG_KEX_DH_GEX_REQUEST: debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); if ((r = sshpkt_get_u32(ssh, &min)) != 0 || (r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_u32(ssh, &max)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; kex->min = min; kex->max = max; min = MAX(DH_GRP_MIN, min); max = MIN(DH_GRP_MAX, max); nbits = MAX(DH_GRP_MIN, nbits); nbits = MIN(DH_GRP_MAX, nbits); break; case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 || (r = sshpkt_get_end(ssh)) != 0) goto out; kex->nbits = nbits; /* unused for old GEX */ kex->min = min = DH_GRP_MIN; kex->max = max = DH_GRP_MAX; break; default: r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (kex->max < kex->min || kex->nbits < kex->min || kex->max < kex->nbits) { r = SSH_ERR_DH_GEX_OUT_OF_RANGE; goto out; } /* Contact privileged parent */ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); if (kex->dh == NULL) { sshpkt_disconnect(ssh, "no matching DH grp found"); r = SSH_ERR_ALLOC_FAIL; goto out; } debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; /* Compute our exchange value in parallel with the client */ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) goto out; /* old KEX does not use min/max in kexgex_hash() */ if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) kex->min = kex->max = -1; debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); r = 0; out: return r; }