diff options
-rw-r--r-- | channels.c | 477 | ||||
-rw-r--r-- | channels.h | 26 | ||||
-rw-r--r-- | mux.c | 6 | ||||
-rw-r--r-- | servconf.c | 138 | ||||
-rw-r--r-- | servconf.h | 16 | ||||
-rw-r--r-- | session.c | 27 | ||||
-rw-r--r-- | ssh.c | 6 |
7 files changed, 439 insertions, 257 deletions
diff --git a/channels.c b/channels.c index 65d9dbd5f..1b40d7da6 100644 --- a/channels.c +++ b/channels.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: channels.c,v 1.380 2018/04/10 00:10:49 djm Exp $ */ | 1 | /* $OpenBSD: channels.c,v 1.381 2018/06/06 18:22:41 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -82,6 +82,7 @@ | |||
82 | #include "key.h" | 82 | #include "key.h" |
83 | #include "authfd.h" | 83 | #include "authfd.h" |
84 | #include "pathnames.h" | 84 | #include "pathnames.h" |
85 | #include "match.h" | ||
85 | 86 | ||
86 | /* -- agent forwarding */ | 87 | /* -- agent forwarding */ |
87 | #define NUM_SOCKS 10 | 88 | #define NUM_SOCKS 10 |
@@ -97,6 +98,10 @@ | |||
97 | /* Maximum number of fake X11 displays to try. */ | 98 | /* Maximum number of fake X11 displays to try. */ |
98 | #define MAX_DISPLAYS 1000 | 99 | #define MAX_DISPLAYS 1000 |
99 | 100 | ||
101 | /* Per-channel callback for pre/post select() actions */ | ||
102 | typedef void chan_fn(struct ssh *, Channel *c, | ||
103 | fd_set *readset, fd_set *writeset); | ||
104 | |||
100 | /* | 105 | /* |
101 | * Data structure for storing which hosts are permitted for forward requests. | 106 | * Data structure for storing which hosts are permitted for forward requests. |
102 | * The local sides of any remote forwards are stored in this array to prevent | 107 | * The local sides of any remote forwards are stored in this array to prevent |
@@ -106,17 +111,40 @@ | |||
106 | /* XXX: streamlocal wants a path instead of host:port */ | 111 | /* XXX: streamlocal wants a path instead of host:port */ |
107 | /* Overload host_to_connect; we could just make this match Forward */ | 112 | /* Overload host_to_connect; we could just make this match Forward */ |
108 | /* XXX - can we use listen_host instead of listen_path? */ | 113 | /* XXX - can we use listen_host instead of listen_path? */ |
109 | typedef struct { | 114 | struct permission { |
110 | char *host_to_connect; /* Connect to 'host'. */ | 115 | char *host_to_connect; /* Connect to 'host'. */ |
111 | int port_to_connect; /* Connect to 'port'. */ | 116 | int port_to_connect; /* Connect to 'port'. */ |
112 | char *listen_host; /* Remote side should listen address. */ | 117 | char *listen_host; /* Remote side should listen address. */ |
113 | char *listen_path; /* Remote side should listen path. */ | 118 | char *listen_path; /* Remote side should listen path. */ |
114 | int listen_port; /* Remote side should listen port. */ | 119 | int listen_port; /* Remote side should listen port. */ |
115 | Channel *downstream; /* Downstream mux*/ | 120 | Channel *downstream; /* Downstream mux*/ |
116 | } ForwardPermission; | 121 | }; |
117 | 122 | ||
118 | typedef void chan_fn(struct ssh *, Channel *c, | 123 | /* |
119 | fd_set *readset, fd_set *writeset); | 124 | * Stores the forwarding permission state for a single direction (local or |
125 | * remote). | ||
126 | */ | ||
127 | struct permission_set { | ||
128 | /* | ||
129 | * List of all local permitted host/port pairs to allow for the | ||
130 | * user. | ||
131 | */ | ||
132 | u_int num_permitted_user; | ||
133 | struct permission *permitted_user; | ||
134 | |||
135 | /* | ||
136 | * List of all permitted host/port pairs to allow for the admin. | ||
137 | */ | ||
138 | u_int num_permitted_admin; | ||
139 | struct permission *permitted_admin; | ||
140 | |||
141 | /* | ||
142 | * If this is true, all opens/listens are permitted. This is the | ||
143 | * case on the server on which we have to trust the client anyway, | ||
144 | * and the user could do anything after logging in. | ||
145 | */ | ||
146 | int all_permitted; | ||
147 | }; | ||
120 | 148 | ||
121 | /* Master structure for channels state */ | 149 | /* Master structure for channels state */ |
122 | struct ssh_channels { | 150 | struct ssh_channels { |
@@ -149,31 +177,8 @@ struct ssh_channels { | |||
149 | chan_fn **channel_post; | 177 | chan_fn **channel_post; |
150 | 178 | ||
151 | /* -- tcp forwarding */ | 179 | /* -- tcp forwarding */ |
152 | 180 | struct permission_set local_perms; | |
153 | /* List of all permitted host/port pairs to connect by the user. */ | 181 | struct permission_set remote_perms; |
154 | ForwardPermission *permitted_opens; | ||
155 | |||
156 | /* List of all permitted host/port pairs to connect by the admin. */ | ||
157 | ForwardPermission *permitted_adm_opens; | ||
158 | |||
159 | /* | ||
160 | * Number of permitted host/port pairs in the array permitted by | ||
161 | * the user. | ||
162 | */ | ||
163 | u_int num_permitted_opens; | ||
164 | |||
165 | /* | ||
166 | * Number of permitted host/port pair in the array permitted by | ||
167 | * the admin. | ||
168 | */ | ||
169 | u_int num_adm_permitted_opens; | ||
170 | |||
171 | /* | ||
172 | * If this is true, all opens are permitted. This is the case on | ||
173 | * the server on which we have to trust the client anyway, and the | ||
174 | * user could do anything after logging in anyway. | ||
175 | */ | ||
176 | int all_opens_permitted; | ||
177 | 182 | ||
178 | /* -- X11 forwarding */ | 183 | /* -- X11 forwarding */ |
179 | 184 | ||
@@ -448,50 +453,95 @@ channel_close_fds(struct ssh *ssh, Channel *c) | |||
448 | } | 453 | } |
449 | 454 | ||
450 | static void | 455 | static void |
451 | fwd_perm_clear(ForwardPermission *fp) | 456 | fwd_perm_clear(struct permission *perm) |
452 | { | 457 | { |
453 | free(fp->host_to_connect); | 458 | free(perm->host_to_connect); |
454 | free(fp->listen_host); | 459 | free(perm->listen_host); |
455 | free(fp->listen_path); | 460 | free(perm->listen_path); |
456 | bzero(fp, sizeof(*fp)); | 461 | bzero(perm, sizeof(*perm)); |
457 | } | 462 | } |
458 | 463 | ||
459 | enum { FWDPERM_USER, FWDPERM_ADMIN }; | 464 | /* Returns an printable name for the specified forwarding permission list */ |
465 | static const char * | ||
466 | fwd_ident(int who, int where) | ||
467 | { | ||
468 | if (who == FORWARD_ADM) { | ||
469 | if (where == FORWARD_LOCAL) | ||
470 | return "admin local"; | ||
471 | else if (where == FORWARD_REMOTE) | ||
472 | return "admin remote"; | ||
473 | } else if (who == FORWARD_USER) { | ||
474 | if (where == FORWARD_LOCAL) | ||
475 | return "user local"; | ||
476 | else if (where == FORWARD_REMOTE) | ||
477 | return "user remote"; | ||
478 | } | ||
479 | fatal("Unknown forward permission list %d/%d", who, where); | ||
480 | } | ||
460 | 481 | ||
461 | static int | 482 | /* Returns the forwarding permission list for the specified direction */ |
462 | fwd_perm_list_add(struct ssh *ssh, int which, | 483 | static struct permission_set * |
463 | const char *host_to_connect, int port_to_connect, | 484 | permission_set_get(struct ssh *ssh, int where) |
464 | const char *listen_host, const char *listen_path, int listen_port, | ||
465 | Channel *downstream) | ||
466 | { | 485 | { |
467 | ForwardPermission **fpl; | 486 | struct ssh_channels *sc = ssh->chanctxt; |
468 | u_int n, *nfpl; | ||
469 | 487 | ||
470 | switch (which) { | 488 | switch (where) { |
471 | case FWDPERM_USER: | 489 | case FORWARD_LOCAL: |
472 | fpl = &ssh->chanctxt->permitted_opens; | 490 | return &sc->local_perms; |
473 | nfpl = &ssh->chanctxt->num_permitted_opens; | ||
474 | break; | 491 | break; |
475 | case FWDPERM_ADMIN: | 492 | case FORWARD_REMOTE: |
476 | fpl = &ssh->chanctxt->permitted_adm_opens; | 493 | return &sc->remote_perms; |
477 | nfpl = &ssh->chanctxt->num_adm_permitted_opens; | ||
478 | break; | 494 | break; |
479 | default: | 495 | default: |
480 | fatal("%s: invalid list %d", __func__, which); | 496 | fatal("%s: invalid forwarding direction %d", __func__, where); |
481 | } | 497 | } |
498 | } | ||
482 | 499 | ||
483 | if (*nfpl >= INT_MAX) | 500 | /* Reutrns pointers to the specified forwarding list and its element count */ |
484 | fatal("%s: overflow", __func__); | 501 | static void |
502 | permission_set_get_array(struct ssh *ssh, int who, int where, | ||
503 | struct permission ***permpp, u_int **npermpp) | ||
504 | { | ||
505 | struct permission_set *pset = permission_set_get(ssh, where); | ||
485 | 506 | ||
486 | *fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl)); | 507 | switch (who) { |
487 | n = (*nfpl)++; | 508 | case FORWARD_USER: |
509 | *permpp = &pset->permitted_user; | ||
510 | *npermpp = &pset->num_permitted_user; | ||
511 | break; | ||
512 | case FORWARD_ADM: | ||
513 | *permpp = &pset->permitted_admin; | ||
514 | *npermpp = &pset->num_permitted_admin; | ||
515 | break; | ||
516 | default: | ||
517 | fatal("%s: invalid forwarding client %d", __func__, who); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | /* Adds an entry to the spcified forwarding list */ | ||
522 | static int | ||
523 | permission_set_add(struct ssh *ssh, int who, int where, | ||
524 | const char *host_to_connect, int port_to_connect, | ||
525 | const char *listen_host, const char *listen_path, int listen_port, | ||
526 | Channel *downstream) | ||
527 | { | ||
528 | struct permission **permp; | ||
529 | u_int n, *npermp; | ||
530 | |||
531 | permission_set_get_array(ssh, who, where, &permp, &npermp); | ||
532 | |||
533 | if (*npermp >= INT_MAX) | ||
534 | fatal("%s: %s overflow", __func__, fwd_ident(who, where)); | ||
535 | |||
536 | *permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp)); | ||
537 | n = (*npermp)++; | ||
488 | #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) | 538 | #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s)) |
489 | (*fpl)[n].host_to_connect = MAYBE_DUP(host_to_connect); | 539 | (*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect); |
490 | (*fpl)[n].port_to_connect = port_to_connect; | 540 | (*permp)[n].port_to_connect = port_to_connect; |
491 | (*fpl)[n].listen_host = MAYBE_DUP(listen_host); | 541 | (*permp)[n].listen_host = MAYBE_DUP(listen_host); |
492 | (*fpl)[n].listen_path = MAYBE_DUP(listen_path); | 542 | (*permp)[n].listen_path = MAYBE_DUP(listen_path); |
493 | (*fpl)[n].listen_port = listen_port; | 543 | (*permp)[n].listen_port = listen_port; |
494 | (*fpl)[n].downstream = downstream; | 544 | (*permp)[n].downstream = downstream; |
495 | #undef MAYBE_DUP | 545 | #undef MAYBE_DUP |
496 | return (int)n; | 546 | return (int)n; |
497 | } | 547 | } |
@@ -500,30 +550,31 @@ static void | |||
500 | mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) | 550 | mux_remove_remote_forwardings(struct ssh *ssh, Channel *c) |
501 | { | 551 | { |
502 | struct ssh_channels *sc = ssh->chanctxt; | 552 | struct ssh_channels *sc = ssh->chanctxt; |
503 | ForwardPermission *fp; | 553 | struct permission_set *pset = &sc->local_perms; |
554 | struct permission *perm; | ||
504 | int r; | 555 | int r; |
505 | u_int i; | 556 | u_int i; |
506 | 557 | ||
507 | for (i = 0; i < sc->num_permitted_opens; i++) { | 558 | for (i = 0; i < pset->num_permitted_user; i++) { |
508 | fp = &sc->permitted_opens[i]; | 559 | perm = &pset->permitted_user[i]; |
509 | if (fp->downstream != c) | 560 | if (perm->downstream != c) |
510 | continue; | 561 | continue; |
511 | 562 | ||
512 | /* cancel on the server, since mux client is gone */ | 563 | /* cancel on the server, since mux client is gone */ |
513 | debug("channel %d: cleanup remote forward for %s:%u", | 564 | debug("channel %d: cleanup remote forward for %s:%u", |
514 | c->self, fp->listen_host, fp->listen_port); | 565 | c->self, perm->listen_host, perm->listen_port); |
515 | if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || | 566 | if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || |
516 | (r = sshpkt_put_cstring(ssh, | 567 | (r = sshpkt_put_cstring(ssh, |
517 | "cancel-tcpip-forward")) != 0 || | 568 | "cancel-tcpip-forward")) != 0 || |
518 | (r = sshpkt_put_u8(ssh, 0)) != 0 || | 569 | (r = sshpkt_put_u8(ssh, 0)) != 0 || |
519 | (r = sshpkt_put_cstring(ssh, | 570 | (r = sshpkt_put_cstring(ssh, |
520 | channel_rfwd_bind_host(fp->listen_host))) != 0 || | 571 | channel_rfwd_bind_host(perm->listen_host))) != 0 || |
521 | (r = sshpkt_put_u32(ssh, fp->listen_port)) != 0 || | 572 | (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 || |
522 | (r = sshpkt_send(ssh)) != 0) { | 573 | (r = sshpkt_send(ssh)) != 0) { |
523 | fatal("%s: channel %i: %s", __func__, | 574 | fatal("%s: channel %i: %s", __func__, |
524 | c->self, ssh_err(r)); | 575 | c->self, ssh_err(r)); |
525 | } | 576 | } |
526 | fwd_perm_clear(fp); /* unregister */ | 577 | fwd_perm_clear(perm); /* unregister */ |
527 | } | 578 | } |
528 | } | 579 | } |
529 | 580 | ||
@@ -2729,7 +2780,7 @@ channel_proxy_downstream(struct ssh *ssh, Channel *downstream) | |||
2729 | goto out; | 2780 | goto out; |
2730 | } | 2781 | } |
2731 | /* Record that connection to this host/port is permitted. */ | 2782 | /* Record that connection to this host/port is permitted. */ |
2732 | fwd_perm_list_add(ssh, FWDPERM_USER, "<mux>", -1, | 2783 | permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1, |
2733 | listen_host, NULL, (int)listen_port, downstream); | 2784 | listen_host, NULL, (int)listen_port, downstream); |
2734 | listen_host = NULL; | 2785 | listen_host = NULL; |
2735 | break; | 2786 | break; |
@@ -3637,11 +3688,78 @@ channel_setup_local_fwd_listener(struct ssh *ssh, | |||
3637 | } | 3688 | } |
3638 | } | 3689 | } |
3639 | 3690 | ||
3691 | /* Matches a remote forwarding permission against a requested forwarding */ | ||
3692 | static int | ||
3693 | remote_open_match(struct permission *allowed_open, struct Forward *fwd) | ||
3694 | { | ||
3695 | int ret; | ||
3696 | char *lhost; | ||
3697 | |||
3698 | /* XXX add ACLs for streamlocal */ | ||
3699 | if (fwd->listen_path != NULL) | ||
3700 | return 1; | ||
3701 | |||
3702 | if (fwd->listen_host == NULL || allowed_open->listen_host == NULL) | ||
3703 | return 0; | ||
3704 | |||
3705 | if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT && | ||
3706 | allowed_open->listen_port != fwd->listen_port) | ||
3707 | return 0; | ||
3708 | |||
3709 | /* Match hostnames case-insensitively */ | ||
3710 | lhost = xstrdup(fwd->listen_host); | ||
3711 | lowercase(lhost); | ||
3712 | ret = match_pattern(lhost, allowed_open->listen_host); | ||
3713 | free(lhost); | ||
3714 | |||
3715 | return ret; | ||
3716 | } | ||
3717 | |||
3718 | /* Checks whether a requested remote forwarding is permitted */ | ||
3719 | static int | ||
3720 | check_rfwd_permission(struct ssh *ssh, struct Forward *fwd) | ||
3721 | { | ||
3722 | struct ssh_channels *sc = ssh->chanctxt; | ||
3723 | struct permission_set *pset = &sc->remote_perms; | ||
3724 | u_int i, permit, permit_adm = 1; | ||
3725 | struct permission *perm; | ||
3726 | |||
3727 | /* XXX apply GatewayPorts override before checking? */ | ||
3728 | |||
3729 | permit = pset->all_permitted; | ||
3730 | if (!permit) { | ||
3731 | for (i = 0; i < pset->num_permitted_user; i++) { | ||
3732 | perm = &pset->permitted_user[i]; | ||
3733 | if (remote_open_match(perm, fwd)) { | ||
3734 | permit = 1; | ||
3735 | break; | ||
3736 | } | ||
3737 | } | ||
3738 | } | ||
3739 | |||
3740 | if (pset->num_permitted_admin > 0) { | ||
3741 | permit_adm = 0; | ||
3742 | for (i = 0; i < pset->num_permitted_admin; i++) { | ||
3743 | perm = &pset->permitted_admin[i]; | ||
3744 | if (remote_open_match(perm, fwd)) { | ||
3745 | permit_adm = 1; | ||
3746 | break; | ||
3747 | } | ||
3748 | } | ||
3749 | } | ||
3750 | |||
3751 | return permit && permit_adm; | ||
3752 | } | ||
3753 | |||
3640 | /* protocol v2 remote port fwd, used by sshd */ | 3754 | /* protocol v2 remote port fwd, used by sshd */ |
3641 | int | 3755 | int |
3642 | channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, | 3756 | channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd, |
3643 | int *allocated_listen_port, struct ForwardOptions *fwd_opts) | 3757 | int *allocated_listen_port, struct ForwardOptions *fwd_opts) |
3644 | { | 3758 | { |
3759 | if (!check_rfwd_permission(ssh, fwd)) { | ||
3760 | packet_send_debug("port forwarding refused"); | ||
3761 | return 0; | ||
3762 | } | ||
3645 | if (fwd->listen_path != NULL) { | 3763 | if (fwd->listen_path != NULL) { |
3646 | return channel_setup_fwd_listener_streamlocal(ssh, | 3764 | return channel_setup_fwd_listener_streamlocal(ssh, |
3647 | SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); | 3765 | SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); |
@@ -3671,7 +3789,7 @@ channel_rfwd_bind_host(const char *listen_host) | |||
3671 | * Initiate forwarding of connections to port "port" on remote host through | 3789 | * Initiate forwarding of connections to port "port" on remote host through |
3672 | * the secure channel to host:port from local side. | 3790 | * the secure channel to host:port from local side. |
3673 | * Returns handle (index) for updating the dynamic listen port with | 3791 | * Returns handle (index) for updating the dynamic listen port with |
3674 | * channel_update_permitted_opens(). | 3792 | * channel_update_permission(). |
3675 | */ | 3793 | */ |
3676 | int | 3794 | int |
3677 | channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) | 3795 | channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) |
@@ -3724,7 +3842,7 @@ channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) | |||
3724 | listen_host = xstrdup(fwd->listen_host); | 3842 | listen_host = xstrdup(fwd->listen_host); |
3725 | listen_port = fwd->listen_port; | 3843 | listen_port = fwd->listen_port; |
3726 | } | 3844 | } |
3727 | idx = fwd_perm_list_add(ssh, FWDPERM_USER, | 3845 | idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, |
3728 | host_to_connect, port_to_connect, | 3846 | host_to_connect, port_to_connect, |
3729 | listen_host, listen_path, listen_port, NULL); | 3847 | listen_host, listen_path, listen_port, NULL); |
3730 | } | 3848 | } |
@@ -3732,7 +3850,7 @@ channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd) | |||
3732 | } | 3850 | } |
3733 | 3851 | ||
3734 | static int | 3852 | static int |
3735 | open_match(ForwardPermission *allowed_open, const char *requestedhost, | 3853 | open_match(struct permission *allowed_open, const char *requestedhost, |
3736 | int requestedport) | 3854 | int requestedport) |
3737 | { | 3855 | { |
3738 | if (allowed_open->host_to_connect == NULL) | 3856 | if (allowed_open->host_to_connect == NULL) |
@@ -3753,7 +3871,7 @@ open_match(ForwardPermission *allowed_open, const char *requestedhost, | |||
3753 | * and what we've sent to the remote server (channel_rfwd_bind_host) | 3871 | * and what we've sent to the remote server (channel_rfwd_bind_host) |
3754 | */ | 3872 | */ |
3755 | static int | 3873 | static int |
3756 | open_listen_match_tcpip(ForwardPermission *allowed_open, | 3874 | open_listen_match_tcpip(struct permission *allowed_open, |
3757 | const char *requestedhost, u_short requestedport, int translate) | 3875 | const char *requestedhost, u_short requestedport, int translate) |
3758 | { | 3876 | { |
3759 | const char *allowed_host; | 3877 | const char *allowed_host; |
@@ -3775,7 +3893,7 @@ open_listen_match_tcpip(ForwardPermission *allowed_open, | |||
3775 | } | 3893 | } |
3776 | 3894 | ||
3777 | static int | 3895 | static int |
3778 | open_listen_match_streamlocal(ForwardPermission *allowed_open, | 3896 | open_listen_match_streamlocal(struct permission *allowed_open, |
3779 | const char *requestedpath) | 3897 | const char *requestedpath) |
3780 | { | 3898 | { |
3781 | if (allowed_open->host_to_connect == NULL) | 3899 | if (allowed_open->host_to_connect == NULL) |
@@ -3797,17 +3915,18 @@ channel_request_rforward_cancel_tcpip(struct ssh *ssh, | |||
3797 | const char *host, u_short port) | 3915 | const char *host, u_short port) |
3798 | { | 3916 | { |
3799 | struct ssh_channels *sc = ssh->chanctxt; | 3917 | struct ssh_channels *sc = ssh->chanctxt; |
3918 | struct permission_set *pset = &sc->local_perms; | ||
3800 | int r; | 3919 | int r; |
3801 | u_int i; | 3920 | u_int i; |
3802 | ForwardPermission *fp; | 3921 | struct permission *perm; |
3803 | 3922 | ||
3804 | for (i = 0; i < sc->num_permitted_opens; i++) { | 3923 | for (i = 0; i < pset->num_permitted_user; i++) { |
3805 | fp = &sc->permitted_opens[i]; | 3924 | perm = &pset->permitted_user[i]; |
3806 | if (open_listen_match_tcpip(fp, host, port, 0)) | 3925 | if (open_listen_match_tcpip(perm, host, port, 0)) |
3807 | break; | 3926 | break; |
3808 | fp = NULL; | 3927 | perm = NULL; |
3809 | } | 3928 | } |
3810 | if (fp == NULL) { | 3929 | if (perm == NULL) { |
3811 | debug("%s: requested forward not found", __func__); | 3930 | debug("%s: requested forward not found", __func__); |
3812 | return -1; | 3931 | return -1; |
3813 | } | 3932 | } |
@@ -3819,7 +3938,7 @@ channel_request_rforward_cancel_tcpip(struct ssh *ssh, | |||
3819 | (r = sshpkt_send(ssh)) != 0) | 3938 | (r = sshpkt_send(ssh)) != 0) |
3820 | fatal("%s: send cancel: %s", __func__, ssh_err(r)); | 3939 | fatal("%s: send cancel: %s", __func__, ssh_err(r)); |
3821 | 3940 | ||
3822 | fwd_perm_clear(fp); /* unregister */ | 3941 | fwd_perm_clear(perm); /* unregister */ |
3823 | 3942 | ||
3824 | return 0; | 3943 | return 0; |
3825 | } | 3944 | } |
@@ -3832,17 +3951,18 @@ static int | |||
3832 | channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) | 3951 | channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) |
3833 | { | 3952 | { |
3834 | struct ssh_channels *sc = ssh->chanctxt; | 3953 | struct ssh_channels *sc = ssh->chanctxt; |
3954 | struct permission_set *pset = &sc->local_perms; | ||
3835 | int r; | 3955 | int r; |
3836 | u_int i; | 3956 | u_int i; |
3837 | ForwardPermission *fp; | 3957 | struct permission *perm; |
3838 | 3958 | ||
3839 | for (i = 0; i < sc->num_permitted_opens; i++) { | 3959 | for (i = 0; i < pset->num_permitted_user; i++) { |
3840 | fp = &sc->permitted_opens[i]; | 3960 | perm = &pset->permitted_user[i]; |
3841 | if (open_listen_match_streamlocal(fp, path)) | 3961 | if (open_listen_match_streamlocal(perm, path)) |
3842 | break; | 3962 | break; |
3843 | fp = NULL; | 3963 | perm = NULL; |
3844 | } | 3964 | } |
3845 | if (fp == NULL) { | 3965 | if (perm == NULL) { |
3846 | debug("%s: requested forward not found", __func__); | 3966 | debug("%s: requested forward not found", __func__); |
3847 | return -1; | 3967 | return -1; |
3848 | } | 3968 | } |
@@ -3854,7 +3974,7 @@ channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path) | |||
3854 | (r = sshpkt_send(ssh)) != 0) | 3974 | (r = sshpkt_send(ssh)) != 0) |
3855 | fatal("%s: send cancel: %s", __func__, ssh_err(r)); | 3975 | fatal("%s: send cancel: %s", __func__, ssh_err(r)); |
3856 | 3976 | ||
3857 | fwd_perm_clear(fp); /* unregister */ | 3977 | fwd_perm_clear(perm); /* unregister */ |
3858 | 3978 | ||
3859 | return 0; | 3979 | return 0; |
3860 | } | 3980 | } |
@@ -3876,25 +3996,64 @@ channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd) | |||
3876 | } | 3996 | } |
3877 | 3997 | ||
3878 | /* | 3998 | /* |
3879 | * Permits opening to any host/port if permitted_opens[] is empty. This is | 3999 | * Permits opening to any host/port if permitted_user[] is empty. This is |
3880 | * usually called by the server, because the user could connect to any port | 4000 | * usually called by the server, because the user could connect to any port |
3881 | * anyway, and the server has no way to know but to trust the client anyway. | 4001 | * anyway, and the server has no way to know but to trust the client anyway. |
3882 | */ | 4002 | */ |
3883 | void | 4003 | void |
3884 | channel_permit_all_opens(struct ssh *ssh) | 4004 | channel_permit_all(struct ssh *ssh, int where) |
3885 | { | 4005 | { |
3886 | if (ssh->chanctxt->num_permitted_opens == 0) | 4006 | struct permission_set *pset = permission_set_get(ssh, where); |
3887 | ssh->chanctxt->all_opens_permitted = 1; | 4007 | |
4008 | if (pset->num_permitted_user == 0) | ||
4009 | pset->all_permitted = 1; | ||
3888 | } | 4010 | } |
3889 | 4011 | ||
4012 | /* | ||
4013 | * Permit the specified host/port for forwarding. | ||
4014 | */ | ||
3890 | void | 4015 | void |
3891 | channel_add_permitted_opens(struct ssh *ssh, char *host, int port) | 4016 | channel_add_permission(struct ssh *ssh, int who, int where, |
4017 | char *host, int port) | ||
3892 | { | 4018 | { |
3893 | struct ssh_channels *sc = ssh->chanctxt; | 4019 | int local = where == FORWARD_LOCAL; |
4020 | struct permission_set *pset = permission_set_get(ssh, where); | ||
3894 | 4021 | ||
3895 | debug("allow port forwarding to host %s port %d", host, port); | 4022 | debug("allow %s forwarding to host %s port %d", |
3896 | fwd_perm_list_add(ssh, FWDPERM_USER, host, port, NULL, NULL, 0, NULL); | 4023 | fwd_ident(who, where), host, port); |
3897 | sc->all_opens_permitted = 0; | 4024 | /* |
4025 | * Remote forwards set listen_host/port, local forwards set | ||
4026 | * host/port_to_connect. | ||
4027 | */ | ||
4028 | permission_set_add(ssh, who, where, | ||
4029 | local ? host : 0, local ? port : 0, | ||
4030 | local ? NULL : host, NULL, local ? 0 : port, NULL); | ||
4031 | pset->all_permitted = 0; | ||
4032 | } | ||
4033 | |||
4034 | /* | ||
4035 | * Administratively disable forwarding. | ||
4036 | */ | ||
4037 | void | ||
4038 | channel_disable_admin(struct ssh *ssh, int where) | ||
4039 | { | ||
4040 | channel_clear_permission(ssh, FORWARD_ADM, where); | ||
4041 | permission_set_add(ssh, FORWARD_ADM, where, | ||
4042 | NULL, 0, NULL, NULL, 0, NULL); | ||
4043 | } | ||
4044 | |||
4045 | /* | ||
4046 | * Clear a list of permitted opens. | ||
4047 | */ | ||
4048 | void | ||
4049 | channel_clear_permission(struct ssh *ssh, int who, int where) | ||
4050 | { | ||
4051 | struct permission **permp; | ||
4052 | u_int *npermp; | ||
4053 | |||
4054 | permission_set_get_array(ssh, who, where, &permp, &npermp); | ||
4055 | *permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp)); | ||
4056 | *npermp = 0; | ||
3898 | } | 4057 | } |
3899 | 4058 | ||
3900 | /* | 4059 | /* |
@@ -3903,63 +4062,28 @@ channel_add_permitted_opens(struct ssh *ssh, char *host, int port) | |||
3903 | * passed then they entry will be invalidated. | 4062 | * passed then they entry will be invalidated. |
3904 | */ | 4063 | */ |
3905 | void | 4064 | void |
3906 | channel_update_permitted_opens(struct ssh *ssh, int idx, int newport) | 4065 | channel_update_permission(struct ssh *ssh, int idx, int newport) |
3907 | { | 4066 | { |
3908 | struct ssh_channels *sc = ssh->chanctxt; | 4067 | struct permission_set *pset = &ssh->chanctxt->local_perms; |
3909 | 4068 | ||
3910 | if (idx < 0 || (u_int)idx >= sc->num_permitted_opens) { | 4069 | if (idx < 0 || (u_int)idx >= pset->num_permitted_user) { |
3911 | debug("%s: index out of range: %d num_permitted_opens %d", | 4070 | debug("%s: index out of range: %d num_permitted_user %d", |
3912 | __func__, idx, sc->num_permitted_opens); | 4071 | __func__, idx, pset->num_permitted_user); |
3913 | return; | 4072 | return; |
3914 | } | 4073 | } |
3915 | debug("%s allowed port %d for forwarding to host %s port %d", | 4074 | debug("%s allowed port %d for forwarding to host %s port %d", |
3916 | newport > 0 ? "Updating" : "Removing", | 4075 | newport > 0 ? "Updating" : "Removing", |
3917 | newport, | 4076 | newport, |
3918 | sc->permitted_opens[idx].host_to_connect, | 4077 | pset->permitted_user[idx].host_to_connect, |
3919 | sc->permitted_opens[idx].port_to_connect); | 4078 | pset->permitted_user[idx].port_to_connect); |
3920 | if (newport <= 0) | 4079 | if (newport <= 0) |
3921 | fwd_perm_clear(&sc->permitted_opens[idx]); | 4080 | fwd_perm_clear(&pset->permitted_user[idx]); |
3922 | else { | 4081 | else { |
3923 | sc->permitted_opens[idx].listen_port = | 4082 | pset->permitted_user[idx].listen_port = |
3924 | (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; | 4083 | (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; |
3925 | } | 4084 | } |
3926 | } | 4085 | } |
3927 | 4086 | ||
3928 | int | ||
3929 | channel_add_adm_permitted_opens(struct ssh *ssh, char *host, int port) | ||
3930 | { | ||
3931 | debug("config allows port forwarding to host %s port %d", host, port); | ||
3932 | return fwd_perm_list_add(ssh, FWDPERM_ADMIN, host, port, | ||
3933 | NULL, NULL, 0, NULL); | ||
3934 | } | ||
3935 | |||
3936 | void | ||
3937 | channel_disable_adm_local_opens(struct ssh *ssh) | ||
3938 | { | ||
3939 | channel_clear_adm_permitted_opens(ssh); | ||
3940 | fwd_perm_list_add(ssh, FWDPERM_ADMIN, NULL, 0, NULL, NULL, 0, NULL); | ||
3941 | } | ||
3942 | |||
3943 | void | ||
3944 | channel_clear_permitted_opens(struct ssh *ssh) | ||
3945 | { | ||
3946 | struct ssh_channels *sc = ssh->chanctxt; | ||
3947 | |||
3948 | sc->permitted_opens = xrecallocarray(sc->permitted_opens, | ||
3949 | sc->num_permitted_opens, 0, sizeof(*sc->permitted_opens)); | ||
3950 | sc->num_permitted_opens = 0; | ||
3951 | } | ||
3952 | |||
3953 | void | ||
3954 | channel_clear_adm_permitted_opens(struct ssh *ssh) | ||
3955 | { | ||
3956 | struct ssh_channels *sc = ssh->chanctxt; | ||
3957 | |||
3958 | sc->permitted_adm_opens = xrecallocarray(sc->permitted_adm_opens, | ||
3959 | sc->num_adm_permitted_opens, 0, sizeof(*sc->permitted_adm_opens)); | ||
3960 | sc->num_adm_permitted_opens = 0; | ||
3961 | } | ||
3962 | |||
3963 | /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ | 4087 | /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ |
3964 | int | 4088 | int |
3965 | permitopen_port(const char *p) | 4089 | permitopen_port(const char *p) |
@@ -4148,19 +4272,21 @@ channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host, | |||
4148 | u_short listen_port, char *ctype, char *rname) | 4272 | u_short listen_port, char *ctype, char *rname) |
4149 | { | 4273 | { |
4150 | struct ssh_channels *sc = ssh->chanctxt; | 4274 | struct ssh_channels *sc = ssh->chanctxt; |
4275 | struct permission_set *pset = &sc->local_perms; | ||
4151 | u_int i; | 4276 | u_int i; |
4152 | ForwardPermission *fp; | 4277 | struct permission *perm; |
4153 | 4278 | ||
4154 | for (i = 0; i < sc->num_permitted_opens; i++) { | 4279 | for (i = 0; i < pset->num_permitted_user; i++) { |
4155 | fp = &sc->permitted_opens[i]; | 4280 | perm = &pset->permitted_user[i]; |
4156 | if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) { | 4281 | if (open_listen_match_tcpip(perm, |
4157 | if (fp->downstream) | 4282 | listen_host, listen_port, 1)) { |
4158 | return fp->downstream; | 4283 | if (perm->downstream) |
4159 | if (fp->port_to_connect == 0) | 4284 | return perm->downstream; |
4285 | if (perm->port_to_connect == 0) | ||
4160 | return rdynamic_connect_prepare(ssh, | 4286 | return rdynamic_connect_prepare(ssh, |
4161 | ctype, rname); | 4287 | ctype, rname); |
4162 | return connect_to(ssh, | 4288 | return connect_to(ssh, |
4163 | fp->host_to_connect, fp->port_to_connect, | 4289 | perm->host_to_connect, perm->port_to_connect, |
4164 | ctype, rname); | 4290 | ctype, rname); |
4165 | } | 4291 | } |
4166 | } | 4292 | } |
@@ -4174,14 +4300,15 @@ channel_connect_by_listen_path(struct ssh *ssh, const char *path, | |||
4174 | char *ctype, char *rname) | 4300 | char *ctype, char *rname) |
4175 | { | 4301 | { |
4176 | struct ssh_channels *sc = ssh->chanctxt; | 4302 | struct ssh_channels *sc = ssh->chanctxt; |
4303 | struct permission_set *pset = &sc->local_perms; | ||
4177 | u_int i; | 4304 | u_int i; |
4178 | ForwardPermission *fp; | 4305 | struct permission *perm; |
4179 | 4306 | ||
4180 | for (i = 0; i < sc->num_permitted_opens; i++) { | 4307 | for (i = 0; i < pset->num_permitted_user; i++) { |
4181 | fp = &sc->permitted_opens[i]; | 4308 | perm = &pset->permitted_user[i]; |
4182 | if (open_listen_match_streamlocal(fp, path)) { | 4309 | if (open_listen_match_streamlocal(perm, path)) { |
4183 | return connect_to(ssh, | 4310 | return connect_to(ssh, |
4184 | fp->host_to_connect, fp->port_to_connect, | 4311 | perm->host_to_connect, perm->port_to_connect, |
4185 | ctype, rname); | 4312 | ctype, rname); |
4186 | } | 4313 | } |
4187 | } | 4314 | } |
@@ -4196,28 +4323,29 @@ channel_connect_to_port(struct ssh *ssh, const char *host, u_short port, | |||
4196 | char *ctype, char *rname, int *reason, const char **errmsg) | 4323 | char *ctype, char *rname, int *reason, const char **errmsg) |
4197 | { | 4324 | { |
4198 | struct ssh_channels *sc = ssh->chanctxt; | 4325 | struct ssh_channels *sc = ssh->chanctxt; |
4326 | struct permission_set *pset = &sc->local_perms; | ||
4199 | struct channel_connect cctx; | 4327 | struct channel_connect cctx; |
4200 | Channel *c; | 4328 | Channel *c; |
4201 | u_int i, permit, permit_adm = 1; | 4329 | u_int i, permit, permit_adm = 1; |
4202 | int sock; | 4330 | int sock; |
4203 | ForwardPermission *fp; | 4331 | struct permission *perm; |
4204 | 4332 | ||
4205 | permit = sc->all_opens_permitted; | 4333 | permit = pset->all_permitted; |
4206 | if (!permit) { | 4334 | if (!permit) { |
4207 | for (i = 0; i < sc->num_permitted_opens; i++) { | 4335 | for (i = 0; i < pset->num_permitted_user; i++) { |
4208 | fp = &sc->permitted_opens[i]; | 4336 | perm = &pset->permitted_user[i]; |
4209 | if (open_match(fp, host, port)) { | 4337 | if (open_match(perm, host, port)) { |
4210 | permit = 1; | 4338 | permit = 1; |
4211 | break; | 4339 | break; |
4212 | } | 4340 | } |
4213 | } | 4341 | } |
4214 | } | 4342 | } |
4215 | 4343 | ||
4216 | if (sc->num_adm_permitted_opens > 0) { | 4344 | if (pset->num_permitted_admin > 0) { |
4217 | permit_adm = 0; | 4345 | permit_adm = 0; |
4218 | for (i = 0; i < sc->num_adm_permitted_opens; i++) { | 4346 | for (i = 0; i < pset->num_permitted_admin; i++) { |
4219 | fp = &sc->permitted_adm_opens[i]; | 4347 | perm = &pset->permitted_admin[i]; |
4220 | if (open_match(fp, host, port)) { | 4348 | if (open_match(perm, host, port)) { |
4221 | permit_adm = 1; | 4349 | permit_adm = 1; |
4222 | break; | 4350 | break; |
4223 | } | 4351 | } |
@@ -4255,25 +4383,26 @@ channel_connect_to_path(struct ssh *ssh, const char *path, | |||
4255 | char *ctype, char *rname) | 4383 | char *ctype, char *rname) |
4256 | { | 4384 | { |
4257 | struct ssh_channels *sc = ssh->chanctxt; | 4385 | struct ssh_channels *sc = ssh->chanctxt; |
4386 | struct permission_set *pset = &sc->local_perms; | ||
4258 | u_int i, permit, permit_adm = 1; | 4387 | u_int i, permit, permit_adm = 1; |
4259 | ForwardPermission *fp; | 4388 | struct permission *perm; |
4260 | 4389 | ||
4261 | permit = sc->all_opens_permitted; | 4390 | permit = pset->all_permitted; |
4262 | if (!permit) { | 4391 | if (!permit) { |
4263 | for (i = 0; i < sc->num_permitted_opens; i++) { | 4392 | for (i = 0; i < pset->num_permitted_user; i++) { |
4264 | fp = &sc->permitted_opens[i]; | 4393 | perm = &pset->permitted_user[i]; |
4265 | if (open_match(fp, path, PORT_STREAMLOCAL)) { | 4394 | if (open_match(perm, path, PORT_STREAMLOCAL)) { |
4266 | permit = 1; | 4395 | permit = 1; |
4267 | break; | 4396 | break; |
4268 | } | 4397 | } |
4269 | } | 4398 | } |
4270 | } | 4399 | } |
4271 | 4400 | ||
4272 | if (sc->num_adm_permitted_opens > 0) { | 4401 | if (pset->num_permitted_admin > 0) { |
4273 | permit_adm = 0; | 4402 | permit_adm = 0; |
4274 | for (i = 0; i < sc->num_adm_permitted_opens; i++) { | 4403 | for (i = 0; i < pset->num_permitted_admin; i++) { |
4275 | fp = &sc->permitted_adm_opens[i]; | 4404 | perm = &pset->permitted_admin[i]; |
4276 | if (open_match(fp, path, PORT_STREAMLOCAL)) { | 4405 | if (open_match(perm, path, PORT_STREAMLOCAL)) { |
4277 | permit_adm = 1; | 4406 | permit_adm = 1; |
4278 | break; | 4407 | break; |
4279 | } | 4408 | } |
diff --git a/channels.h b/channels.h index 126b04345..1aeafe94e 100644 --- a/channels.h +++ b/channels.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: channels.h,v 1.130 2017/09/21 19:16:53 markus Exp $ */ | 1 | /* $OpenBSD: channels.h,v 1.131 2018/06/06 18:22:41 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -63,6 +63,15 @@ | |||
63 | 63 | ||
64 | #define CHANNEL_CANCEL_PORT_STATIC -1 | 64 | #define CHANNEL_CANCEL_PORT_STATIC -1 |
65 | 65 | ||
66 | /* TCP forwarding */ | ||
67 | #define FORWARD_DENY 0 | ||
68 | #define FORWARD_REMOTE (1) | ||
69 | #define FORWARD_LOCAL (1<<1) | ||
70 | #define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) | ||
71 | |||
72 | #define FORWARD_ADM 0x100 | ||
73 | #define FORWARD_USER 0x101 | ||
74 | |||
66 | struct ssh; | 75 | struct ssh; |
67 | struct Channel; | 76 | struct Channel; |
68 | typedef struct Channel Channel; | 77 | typedef struct Channel Channel; |
@@ -283,16 +292,11 @@ int channel_find_open(struct ssh *); | |||
283 | struct Forward; | 292 | struct Forward; |
284 | struct ForwardOptions; | 293 | struct ForwardOptions; |
285 | void channel_set_af(struct ssh *, int af); | 294 | void channel_set_af(struct ssh *, int af); |
286 | void channel_permit_all_opens(struct ssh *); | 295 | void channel_permit_all(struct ssh *, int); |
287 | void channel_add_permitted_opens(struct ssh *, char *, int); | 296 | void channel_add_permission(struct ssh *, int, int, char *, int); |
288 | int channel_add_adm_permitted_opens(struct ssh *, char *, int); | 297 | void channel_clear_permission(struct ssh *, int, int); |
289 | void channel_copy_adm_permitted_opens(struct ssh *, | 298 | void channel_disable_admin(struct ssh *, int); |
290 | const struct fwd_perm_list *); | 299 | void channel_update_permission(struct ssh *, int, int); |
291 | void channel_disable_adm_local_opens(struct ssh *); | ||
292 | void channel_update_permitted_opens(struct ssh *, int, int); | ||
293 | void channel_clear_permitted_opens(struct ssh *); | ||
294 | void channel_clear_adm_permitted_opens(struct ssh *); | ||
295 | void channel_print_adm_permitted_opens(struct ssh *); | ||
296 | Channel *channel_connect_to_port(struct ssh *, const char *, u_short, | 300 | Channel *channel_connect_to_port(struct ssh *, const char *, u_short, |
297 | char *, char *, int *, const char **); | 301 | char *, char *, int *, const char **); |
298 | Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *); | 302 | Channel *channel_connect_to_path(struct ssh *, const char *, char *, char *); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: mux.c,v 1.69 2017/09/20 05:19:00 dtucker Exp $ */ | 1 | /* $OpenBSD: mux.c,v 1.70 2018/06/06 18:22:41 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -634,7 +634,7 @@ mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) | |||
634 | buffer_put_int(&out, MUX_S_REMOTE_PORT); | 634 | buffer_put_int(&out, MUX_S_REMOTE_PORT); |
635 | buffer_put_int(&out, fctx->rid); | 635 | buffer_put_int(&out, fctx->rid); |
636 | buffer_put_int(&out, rfwd->allocated_port); | 636 | buffer_put_int(&out, rfwd->allocated_port); |
637 | channel_update_permitted_opens(ssh, rfwd->handle, | 637 | channel_update_permission(ssh, rfwd->handle, |
638 | rfwd->allocated_port); | 638 | rfwd->allocated_port); |
639 | } else { | 639 | } else { |
640 | buffer_put_int(&out, MUX_S_OK); | 640 | buffer_put_int(&out, MUX_S_OK); |
@@ -643,7 +643,7 @@ mux_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) | |||
643 | goto out; | 643 | goto out; |
644 | } else { | 644 | } else { |
645 | if (rfwd->listen_port == 0) | 645 | if (rfwd->listen_port == 0) |
646 | channel_update_permitted_opens(ssh, rfwd->handle, -1); | 646 | channel_update_permission(ssh, rfwd->handle, -1); |
647 | if (rfwd->listen_path != NULL) | 647 | if (rfwd->listen_path != NULL) |
648 | xasprintf(&failmsg, "remote port forwarding failed for " | 648 | xasprintf(&failmsg, "remote port forwarding failed for " |
649 | "listen path %s", rfwd->listen_path); | 649 | "listen path %s", rfwd->listen_path); |
diff --git a/servconf.c b/servconf.c index 5ca84515f..b75faf3f8 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,5 +1,5 @@ | |||
1 | 1 | ||
2 | /* $OpenBSD: servconf.c,v 1.328 2018/04/10 00:10:49 djm Exp $ */ | 2 | /* $OpenBSD: servconf.c,v 1.329 2018/06/06 18:22:41 djm Exp $ */ |
3 | /* | 3 | /* |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 | * All rights reserved | 5 | * All rights reserved |
@@ -160,6 +160,7 @@ initialize_server_options(ServerOptions *options) | |||
160 | options->num_accept_env = 0; | 160 | options->num_accept_env = 0; |
161 | options->permit_tun = -1; | 161 | options->permit_tun = -1; |
162 | options->permitted_opens = NULL; | 162 | options->permitted_opens = NULL; |
163 | options->permitted_remote_opens = NULL; | ||
163 | options->adm_forced_command = NULL; | 164 | options->adm_forced_command = NULL; |
164 | options->chroot_directory = NULL; | 165 | options->chroot_directory = NULL; |
165 | options->authorized_keys_command = NULL; | 166 | options->authorized_keys_command = NULL; |
@@ -462,7 +463,7 @@ typedef enum { | |||
462 | sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, | 463 | sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, |
463 | sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, | 464 | sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, |
464 | sAcceptEnv, sPermitTunnel, | 465 | sAcceptEnv, sPermitTunnel, |
465 | sMatch, sPermitOpen, sForceCommand, sChrootDirectory, | 466 | sMatch, sPermitOpen, sPermitRemoteOpen, sForceCommand, sChrootDirectory, |
466 | sUsePrivilegeSeparation, sAllowAgentForwarding, | 467 | sUsePrivilegeSeparation, sAllowAgentForwarding, |
467 | sHostCertificate, | 468 | sHostCertificate, |
468 | sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, | 469 | sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, |
@@ -597,6 +598,7 @@ static struct { | |||
597 | { "permituserrc", sPermitUserRC, SSHCFG_ALL }, | 598 | { "permituserrc", sPermitUserRC, SSHCFG_ALL }, |
598 | { "match", sMatch, SSHCFG_ALL }, | 599 | { "match", sMatch, SSHCFG_ALL }, |
599 | { "permitopen", sPermitOpen, SSHCFG_ALL }, | 600 | { "permitopen", sPermitOpen, SSHCFG_ALL }, |
601 | { "permitremoteopen", sPermitRemoteOpen, SSHCFG_ALL }, | ||
600 | { "forcecommand", sForceCommand, SSHCFG_ALL }, | 602 | { "forcecommand", sForceCommand, SSHCFG_ALL }, |
601 | { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, | 603 | { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, |
602 | { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, | 604 | { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, |
@@ -632,6 +634,20 @@ static struct { | |||
632 | { -1, NULL } | 634 | { -1, NULL } |
633 | }; | 635 | }; |
634 | 636 | ||
637 | /* Returns an opcode name from its number */ | ||
638 | |||
639 | static const char * | ||
640 | lookup_opcode_name(ServerOpCodes code) | ||
641 | { | ||
642 | u_int i; | ||
643 | |||
644 | for (i = 0; keywords[i].name != NULL; i++) | ||
645 | if (keywords[i].opcode == code) | ||
646 | return(keywords[i].name); | ||
647 | return "UNKNOWN"; | ||
648 | } | ||
649 | |||
650 | |||
635 | /* | 651 | /* |
636 | * Returns the number of the token pointed to by cp or sBadOption. | 652 | * Returns the number of the token pointed to by cp or sBadOption. |
637 | */ | 653 | */ |
@@ -814,43 +830,59 @@ process_queued_listen_addrs(ServerOptions *options) | |||
814 | } | 830 | } |
815 | 831 | ||
816 | /* | 832 | /* |
817 | * Inform channels layer of permitopen options from configuration. | 833 | * Inform channels layer of permitopen options for a single forwarding |
834 | * direction (local/remote). | ||
818 | */ | 835 | */ |
819 | void | 836 | static void |
820 | process_permitopen(struct ssh *ssh, ServerOptions *options) | 837 | process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode, |
838 | char **opens, u_int num_opens) | ||
821 | { | 839 | { |
822 | u_int i; | 840 | u_int i; |
823 | int port; | 841 | int port; |
824 | char *host, *arg, *oarg; | 842 | char *host, *arg, *oarg; |
843 | int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; | ||
844 | const char *what = lookup_opcode_name(opcode); | ||
825 | 845 | ||
826 | channel_clear_adm_permitted_opens(ssh); | 846 | channel_clear_permission(ssh, FORWARD_ADM, where); |
827 | if (options->num_permitted_opens == 0) | 847 | if (num_opens == 0) |
828 | return; /* permit any */ | 848 | return; /* permit any */ |
829 | 849 | ||
830 | /* handle keywords: "any" / "none" */ | 850 | /* handle keywords: "any" / "none" */ |
831 | if (options->num_permitted_opens == 1 && | 851 | if (num_opens == 1 && strcmp(opens[0], "any") == 0) |
832 | strcmp(options->permitted_opens[0], "any") == 0) | ||
833 | return; | 852 | return; |
834 | if (options->num_permitted_opens == 1 && | 853 | if (num_opens == 1 && strcmp(opens[0], "none") == 0) { |
835 | strcmp(options->permitted_opens[0], "none") == 0) { | 854 | channel_disable_admin(ssh, where); |
836 | channel_disable_adm_local_opens(ssh); | ||
837 | return; | 855 | return; |
838 | } | 856 | } |
839 | /* Otherwise treat it as a list of permitted host:port */ | 857 | /* Otherwise treat it as a list of permitted host:port */ |
840 | for (i = 0; i < options->num_permitted_opens; i++) { | 858 | for (i = 0; i < num_opens; i++) { |
841 | oarg = arg = xstrdup(options->permitted_opens[i]); | 859 | oarg = arg = xstrdup(opens[i]); |
842 | host = hpdelim(&arg); | 860 | host = hpdelim(&arg); |
843 | if (host == NULL) | 861 | if (host == NULL) |
844 | fatal("%s: missing host in PermitOpen", __func__); | 862 | fatal("%s: missing host in %s", __func__, what); |
845 | host = cleanhostname(host); | 863 | host = cleanhostname(host); |
846 | if (arg == NULL || ((port = permitopen_port(arg)) < 0)) | 864 | if (arg == NULL || ((port = permitopen_port(arg)) < 0)) |
847 | fatal("%s: bad port number in PermitOpen", __func__); | 865 | fatal("%s: bad port number in %s", __func__, what); |
848 | /* Send it to channels layer */ | 866 | /* Send it to channels layer */ |
849 | channel_add_adm_permitted_opens(ssh, host, port); | 867 | channel_add_permission(ssh, FORWARD_ADM, |
868 | where, host, port); | ||
850 | free(oarg); | 869 | free(oarg); |
851 | } | 870 | } |
852 | } | 871 | } |
853 | 872 | ||
873 | /* | ||
874 | * Inform channels layer of permitopen options from configuration. | ||
875 | */ | ||
876 | void | ||
877 | process_permitopen(struct ssh *ssh, ServerOptions *options) | ||
878 | { | ||
879 | process_permitopen_list(ssh, sPermitOpen, | ||
880 | options->permitted_opens, options->num_permitted_opens); | ||
881 | process_permitopen_list(ssh, sPermitRemoteOpen, | ||
882 | options->permitted_remote_opens, | ||
883 | options->num_permitted_remote_opens); | ||
884 | } | ||
885 | |||
854 | struct connection_info * | 886 | struct connection_info * |
855 | get_connection_info(int populate, int use_dns) | 887 | get_connection_info(int populate, int use_dns) |
856 | { | 888 | { |
@@ -1144,12 +1176,12 @@ process_server_config_line(ServerOptions *options, char *line, | |||
1144 | const char *filename, int linenum, int *activep, | 1176 | const char *filename, int linenum, int *activep, |
1145 | struct connection_info *connectinfo) | 1177 | struct connection_info *connectinfo) |
1146 | { | 1178 | { |
1147 | char *cp, **charptr, *arg, *arg2, *p; | 1179 | char *cp, ***chararrayptr, **charptr, *arg, *arg2, *p; |
1148 | int cmdline = 0, *intptr, value, value2, n, port; | 1180 | int cmdline = 0, *intptr, value, value2, n, port; |
1149 | SyslogFacility *log_facility_ptr; | 1181 | SyslogFacility *log_facility_ptr; |
1150 | LogLevel *log_level_ptr; | 1182 | LogLevel *log_level_ptr; |
1151 | ServerOpCodes opcode; | 1183 | ServerOpCodes opcode; |
1152 | u_int i, flags = 0; | 1184 | u_int i, *uintptr, uvalue, flags = 0; |
1153 | size_t len; | 1185 | size_t len; |
1154 | long long val64; | 1186 | long long val64; |
1155 | const struct multistate *multistate_ptr; | 1187 | const struct multistate *multistate_ptr; |
@@ -1799,36 +1831,49 @@ process_server_config_line(ServerOptions *options, char *line, | |||
1799 | *activep = value; | 1831 | *activep = value; |
1800 | break; | 1832 | break; |
1801 | 1833 | ||
1834 | case sPermitRemoteOpen: | ||
1802 | case sPermitOpen: | 1835 | case sPermitOpen: |
1836 | if (opcode == sPermitRemoteOpen) { | ||
1837 | uintptr = &options->num_permitted_remote_opens; | ||
1838 | chararrayptr = &options->permitted_remote_opens; | ||
1839 | } else { | ||
1840 | uintptr = &options->num_permitted_opens; | ||
1841 | chararrayptr = &options->permitted_opens; | ||
1842 | } | ||
1803 | arg = strdelim(&cp); | 1843 | arg = strdelim(&cp); |
1804 | if (!arg || *arg == '\0') | 1844 | if (!arg || *arg == '\0') |
1805 | fatal("%s line %d: missing PermitOpen specification", | 1845 | fatal("%s line %d: missing %s specification", |
1806 | filename, linenum); | 1846 | filename, linenum, lookup_opcode_name(opcode)); |
1807 | value = options->num_permitted_opens; /* modified later */ | 1847 | uvalue = *uintptr; /* modified later */ |
1808 | if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { | 1848 | if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) { |
1809 | if (*activep && value == 0) { | 1849 | if (*activep && uvalue == 0) { |
1810 | options->num_permitted_opens = 1; | 1850 | *uintptr = 1; |
1811 | options->permitted_opens = xcalloc(1, | 1851 | *chararrayptr = xcalloc(1, |
1812 | sizeof(*options->permitted_opens)); | 1852 | sizeof(**chararrayptr)); |
1813 | options->permitted_opens[0] = xstrdup(arg); | 1853 | (*chararrayptr)[0] = xstrdup(arg); |
1814 | } | 1854 | } |
1815 | break; | 1855 | break; |
1816 | } | 1856 | } |
1817 | for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { | 1857 | for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { |
1818 | arg2 = xstrdup(arg); | 1858 | arg2 = xstrdup(arg); |
1819 | p = hpdelim(&arg); | 1859 | p = hpdelim(&arg); |
1820 | if (p == NULL) | 1860 | /* XXX support bare port number for PermitRemoteOpen */ |
1821 | fatal("%s line %d: missing host in PermitOpen", | 1861 | if (p == NULL) { |
1822 | filename, linenum); | 1862 | fatal("%s line %d: missing host in %s", |
1863 | filename, linenum, | ||
1864 | lookup_opcode_name(opcode)); | ||
1865 | } | ||
1823 | p = cleanhostname(p); | 1866 | p = cleanhostname(p); |
1824 | if (arg == NULL || ((port = permitopen_port(arg)) < 0)) | 1867 | if (arg == NULL || |
1825 | fatal("%s line %d: bad port number in " | 1868 | ((port = permitopen_port(arg)) < 0)) { |
1826 | "PermitOpen", filename, linenum); | 1869 | fatal("%s line %d: bad port number in %s", |
1827 | if (*activep && value == 0) { | 1870 | filename, linenum, |
1871 | lookup_opcode_name(opcode)); | ||
1872 | } | ||
1873 | if (*activep && uvalue == 0) { | ||
1828 | array_append(filename, linenum, | 1874 | array_append(filename, linenum, |
1829 | "PermitOpen", | 1875 | lookup_opcode_name(opcode), |
1830 | &options->permitted_opens, | 1876 | chararrayptr, uintptr, arg2); |
1831 | &options->num_permitted_opens, arg2); | ||
1832 | } | 1877 | } |
1833 | free(arg2); | 1878 | free(arg2); |
1834 | } | 1879 | } |
@@ -2307,17 +2352,6 @@ fmt_intarg(ServerOpCodes code, int val) | |||
2307 | } | 2352 | } |
2308 | } | 2353 | } |
2309 | 2354 | ||
2310 | static const char * | ||
2311 | lookup_opcode_name(ServerOpCodes code) | ||
2312 | { | ||
2313 | u_int i; | ||
2314 | |||
2315 | for (i = 0; keywords[i].name != NULL; i++) | ||
2316 | if (keywords[i].opcode == code) | ||
2317 | return(keywords[i].name); | ||
2318 | return "UNKNOWN"; | ||
2319 | } | ||
2320 | |||
2321 | static void | 2355 | static void |
2322 | dump_cfg_int(ServerOpCodes code, int val) | 2356 | dump_cfg_int(ServerOpCodes code, int val) |
2323 | { | 2357 | { |
@@ -2562,4 +2596,12 @@ dump_config(ServerOptions *o) | |||
2562 | printf(" %s", o->permitted_opens[i]); | 2596 | printf(" %s", o->permitted_opens[i]); |
2563 | } | 2597 | } |
2564 | printf("\n"); | 2598 | printf("\n"); |
2599 | printf("permitremoteopen"); | ||
2600 | if (o->num_permitted_remote_opens == 0) | ||
2601 | printf(" any"); | ||
2602 | else { | ||
2603 | for (i = 0; i < o->num_permitted_remote_opens; i++) | ||
2604 | printf(" %s", o->permitted_remote_opens[i]); | ||
2605 | } | ||
2606 | printf("\n"); | ||
2565 | } | 2607 | } |
diff --git a/servconf.h b/servconf.h index 6d2553c38..62acd8938 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: servconf.h,v 1.131 2018/04/13 03:57:26 dtucker Exp $ */ | 1 | /* $OpenBSD: servconf.h,v 1.132 2018/06/06 18:22:41 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -32,12 +32,6 @@ | |||
32 | #define PRIVSEP_ON 1 | 32 | #define PRIVSEP_ON 1 |
33 | #define PRIVSEP_NOSANDBOX 2 | 33 | #define PRIVSEP_NOSANDBOX 2 |
34 | 34 | ||
35 | /* AllowTCPForwarding */ | ||
36 | #define FORWARD_DENY 0 | ||
37 | #define FORWARD_REMOTE (1) | ||
38 | #define FORWARD_LOCAL (1<<1) | ||
39 | #define FORWARD_ALLOW (FORWARD_REMOTE|FORWARD_LOCAL) | ||
40 | |||
41 | /* PermitOpen */ | 35 | /* PermitOpen */ |
42 | #define PERMITOPEN_ANY 0 | 36 | #define PERMITOPEN_ANY 0 |
43 | #define PERMITOPEN_NONE -2 | 37 | #define PERMITOPEN_NONE -2 |
@@ -187,8 +181,10 @@ typedef struct { | |||
187 | 181 | ||
188 | int permit_tun; | 182 | int permit_tun; |
189 | 183 | ||
190 | char **permitted_opens; | 184 | char **permitted_opens; /* May also be one of PERMITOPEN_* */ |
191 | u_int num_permitted_opens; /* May also be one of PERMITOPEN_* */ | 185 | u_int num_permitted_opens; |
186 | char **permitted_remote_opens; /* May also be one of PERMITOPEN_* */ | ||
187 | u_int num_permitted_remote_opens; | ||
192 | 188 | ||
193 | char *chroot_directory; | 189 | char *chroot_directory; |
194 | char *revoked_keys_file; | 190 | char *revoked_keys_file; |
@@ -252,6 +248,8 @@ struct connection_info { | |||
252 | M_CP_STRARRAYOPT(accept_env, num_accept_env); \ | 248 | M_CP_STRARRAYOPT(accept_env, num_accept_env); \ |
253 | M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ | 249 | M_CP_STRARRAYOPT(auth_methods, num_auth_methods); \ |
254 | M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ | 250 | M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens); \ |
251 | M_CP_STRARRAYOPT(permitted_remote_opens, \ | ||
252 | num_permitted_remote_opens); \ | ||
255 | } while (0) | 253 | } while (0) |
256 | 254 | ||
257 | struct connection_info *get_connection_info(int, int); | 255 | struct connection_info *get_connection_info(int, int); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: session.c,v 1.295 2018/06/01 03:33:53 djm Exp $ */ | 1 | /* $OpenBSD: session.c,v 1.296 2018/06/06 18:22:41 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | * All rights reserved | 4 | * All rights reserved |
@@ -298,7 +298,7 @@ set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) | |||
298 | 298 | ||
299 | if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) | 299 | if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) |
300 | return; | 300 | return; |
301 | channel_clear_permitted_opens(ssh); | 301 | channel_clear_permission(ssh, FORWARD_USER, FORWARD_LOCAL); |
302 | for (i = 0; i < auth_opts->npermitopen; i++) { | 302 | for (i = 0; i < auth_opts->npermitopen; i++) { |
303 | tmp = cp = xstrdup(auth_opts->permitopen[i]); | 303 | tmp = cp = xstrdup(auth_opts->permitopen[i]); |
304 | /* This shouldn't fail as it has already been checked */ | 304 | /* This shouldn't fail as it has already been checked */ |
@@ -308,7 +308,8 @@ set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts) | |||
308 | if (cp == NULL || (port = permitopen_port(cp)) < 0) | 308 | if (cp == NULL || (port = permitopen_port(cp)) < 0) |
309 | fatal("%s: internal error: permitopen port", | 309 | fatal("%s: internal error: permitopen port", |
310 | __func__); | 310 | __func__); |
311 | channel_add_permitted_opens(ssh, host, port); | 311 | channel_add_permission(ssh, FORWARD_USER, FORWARD_LOCAL, |
312 | host, port); | ||
312 | free(tmp); | 313 | free(tmp); |
313 | } | 314 | } |
314 | } | 315 | } |
@@ -323,13 +324,21 @@ do_authenticated(struct ssh *ssh, Authctxt *authctxt) | |||
323 | /* setup the channel layer */ | 324 | /* setup the channel layer */ |
324 | /* XXX - streamlocal? */ | 325 | /* XXX - streamlocal? */ |
325 | set_permitopen_from_authopts(ssh, auth_opts); | 326 | set_permitopen_from_authopts(ssh, auth_opts); |
326 | if (!auth_opts->permit_port_forwarding_flag || | ||
327 | options.disable_forwarding || | ||
328 | (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) | ||
329 | channel_disable_adm_local_opens(ssh); | ||
330 | else | ||
331 | channel_permit_all_opens(ssh); | ||
332 | 327 | ||
328 | if (!auth_opts->permit_port_forwarding_flag || | ||
329 | options.disable_forwarding) { | ||
330 | channel_disable_admin(ssh, FORWARD_LOCAL); | ||
331 | channel_disable_admin(ssh, FORWARD_REMOTE); | ||
332 | } else { | ||
333 | if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) | ||
334 | channel_disable_admin(ssh, FORWARD_LOCAL); | ||
335 | else | ||
336 | channel_permit_all(ssh, FORWARD_LOCAL); | ||
337 | if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0) | ||
338 | channel_disable_admin(ssh, FORWARD_REMOTE); | ||
339 | else | ||
340 | channel_permit_all(ssh, FORWARD_REMOTE); | ||
341 | } | ||
333 | auth_debug_send(); | 342 | auth_debug_send(); |
334 | 343 | ||
335 | prepare_auth_info_file(authctxt->pw, authctxt->session_info); | 344 | prepare_auth_info_file(authctxt->pw, authctxt->session_info); |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.479 2018/06/01 03:33:53 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.480 2018/06/06 18:22:41 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -1654,10 +1654,10 @@ ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) | |||
1654 | logit("Allocated port %u for remote forward to %s:%d", | 1654 | logit("Allocated port %u for remote forward to %s:%d", |
1655 | rfwd->allocated_port, | 1655 | rfwd->allocated_port, |
1656 | rfwd->connect_host, rfwd->connect_port); | 1656 | rfwd->connect_host, rfwd->connect_port); |
1657 | channel_update_permitted_opens(ssh, | 1657 | channel_update_permission(ssh, |
1658 | rfwd->handle, rfwd->allocated_port); | 1658 | rfwd->handle, rfwd->allocated_port); |
1659 | } else { | 1659 | } else { |
1660 | channel_update_permitted_opens(ssh, rfwd->handle, -1); | 1660 | channel_update_permission(ssh, rfwd->handle, -1); |
1661 | } | 1661 | } |
1662 | } | 1662 | } |
1663 | 1663 | ||