summaryrefslogtreecommitdiff
path: root/addrmatch.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
committerColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
commit137d76ba65883aa8143af1fcad83b57e7badef0c (patch)
treef426e804bb5248ceafedfab7bb78ae6e6752942c /addrmatch.c
parentdac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff)
parentef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff)
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1: - 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9): + Added chroot(2) support for sshd(8), controlled by a new option "ChrootDirectory" (closes: #139047, LP: #24777). + Linked sftp-server(8) into sshd(8). The internal sftp server is used when the command "internal-sftp" is specified in a Subsystem or ForceCommand declaration. When used with ChrootDirectory, the internal sftp server requires no special configuration of files inside the chroot environment. + Added a protocol extension method "posix-rename@openssh.com" for sftp-server(8) to perform POSIX atomic rename() operations; sftp(1) prefers this if available (closes: #308561). + Removed the fixed limit of 100 file handles in sftp-server(8). + ssh(8) will now skip generation of SSH protocol 1 ephemeral server keys when in inetd mode and protocol 2 connections are negotiated. This speeds up protocol 2 connections to inetd-mode servers that also allow Protocol 1. + Accept the PermitRootLogin directive in a sshd_config(5) Match block. Allows for, e.g. permitting root only from the local network. + Reworked sftp(1) argument splitting and escaping to be more internally consistent (i.e. between sftp commands) and more consistent with sh(1). Please note that this will change the interpretation of some quoted strings, especially those with embedded backslash escape sequences. + Support "Banner=none" in sshd_config(5) to disable sending of a pre-login banner (e.g. in a Match block). + ssh(1) ProxyCommands are now executed with $SHELL rather than /bin/sh. + ssh(1)'s ConnectTimeout option is now applied to both the TCP connection and the SSH banner exchange (previously it just covered the TCP connection). This allows callers of ssh(1) to better detect and deal with stuck servers that accept a TCP connection but don't progress the protocol, and also makes ConnectTimeout useful for connections via a ProxyCommand. + scp(1) incorrectly reported "stalled" on slow copies (closes: #140828). + scp(1) date underflow for timestamps before epoch. + ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS, instead of the current standard RRSIG. + Correctly drain ACKs when a sftp(1) upload write fails midway, avoids a fatal() exit from what should be a recoverable condition. + Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF hostname") to not include any IP address in the data to be hashed. + Make ssh(1) skip listening on the IPv6 wildcard address when a binding address of 0.0.0.0 is used against an old SSH server that does not support the RFC4254 syntax for wildcard bind addresses. + Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is already done for X11/TCP forwarding sockets (closes: #439661). + Fix FD leak that could hang a ssh(1) connection multiplexing master. + Make ssh(1) -q option documentation consistent with reality. + Fixed sshd(8) PAM support not calling pam_session_close(), or failing to call it with root privileges (closes: #372680). + Fix activation of OpenSSL engine support when requested in configure (LP: #119295). - 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1): + Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1) and ssh-keygen(1). Visual fingerprint display is controlled by a new ssh_config(5) option "VisualHostKey". The intent is to render SSH host keys in a visual form that is amenable to easy recall and rejection of changed host keys. + sshd_config(5) now supports CIDR address/masklen matching in "Match address" blocks, with a fallback to classic wildcard matching. + sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys from="..." restrictions, also with a fallback to classic wildcard matching. + Added an extended test mode (-T) to sshd(8) to request that it write its effective configuration to stdout and exit. Extended test mode also supports the specification of connection parameters (username, source address and hostname) to test the application of sshd_config(5) Match rules. + ssh(1) now prints the number of bytes transferred and the overall connection throughput for SSH protocol 2 sessions when in verbose mode (previously these statistics were displayed for protocol 1 connections only). + sftp-server(8) now supports extension methods statvfs@openssh.com and fstatvfs@openssh.com that implement statvfs(2)-like operations. + sftp(1) now has a "df" command to the sftp client that uses the statvfs@openssh.com to produce a df(1)-like display of filesystem space and inode utilisation (requires statvfs@openssh.com support on the server). + Added a MaxSessions option to sshd_config(5) to allow control of the number of multiplexed sessions supported over a single TCP connection. This allows increasing the number of allowed sessions above the previous default of 10, disabling connection multiplexing (MaxSessions=1) or disallowing login/shell/subsystem sessions entirely (MaxSessions=0). + Added a no-more-sessions@openssh.com global request extension that is sent from ssh(1) to sshd(8) when the client knows that it will never request another session (i.e. when session multiplexing is disabled). This allows a server to disallow further session requests and terminate the session in cases where the client has been hijacked. + ssh-keygen(1) now supports the use of the -l option in combination with -F to search for a host in ~/.ssh/known_hosts and display its fingerprint. + ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of "rsa1". + Added an AllowAgentForwarding option to sshd_config(8) to control whether authentication agent forwarding is permitted. Note that this is a loose control, as a client may install their own unofficial forwarder. + ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when receiving network data, resulting in a ~10% speedup. + ssh(1) and sshd(8) will now try additional addresses when connecting to a port forward destination whose DNS name resolves to more than one address. The previous behaviour was to try the only first address and give up if that failed. + ssh(1) and sshd(8) now support signalling that channels are half-closed for writing, through a channel protocol extension notification "eow@openssh.com". This allows propagation of closed file descriptors, so that commands such as "ssh -2 localhost od /bin/ls | true" do not send unnecessary data over the wire. + sshd(8): increased the default size of ssh protocol 1 ephemeral keys from 768 to 1024 bits. + When ssh(1) has been requested to fork after authentication ("ssh -f") with ExitOnForwardFailure enabled, delay the fork until after replies for any -R forwards have been seen. Allows for robust detection of -R forward failure when using -f. + "Match group" blocks in sshd_config(5) now support negation of groups. E.g. "Match group staff,!guests". + sftp(1) and sftp-server(8) now allow chmod-like operations to set set[ug]id/sticky bits. + The MaxAuthTries option is now permitted in sshd_config(5) match blocks. + Multiplexed ssh(1) sessions now support a subset of the ~ escapes that are available to a primary connection. + ssh(1) connection multiplexing will now fall back to creating a new connection in most error cases (closes: #352830). + Make ssh(1) deal more gracefully with channel requests that fail. Previously it would optimistically assume that requests would always succeed, which could cause hangs if they did not (e.g. when the server runs out of file descriptors). + ssh(1) now reports multiplexing errors via the multiplex slave's stderr where possible (subject to LogLevel in the mux master). + Prevent sshd(8) from erroneously applying public key restrictions leaned from ~/.ssh/authorized_keys to other authentication methods when public key authentication subsequently fails (LP: #161047). + Fixed an UMAC alignment problem that manifested on Itanium platforms.
Diffstat (limited to 'addrmatch.c')
-rw-r--r--addrmatch.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/addrmatch.c b/addrmatch.c
new file mode 100644
index 000000000..2086afe84
--- /dev/null
+++ b/addrmatch.c
@@ -0,0 +1,421 @@
1/* $OpenBSD: addrmatch.c,v 1.3 2008/06/10 23:06:19 djm Exp $ */
2
3/*
4 * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include "includes.h"
20
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25
26#include <netdb.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <stdarg.h>
31
32#include "match.h"
33#include "log.h"
34
35struct xaddr {
36 sa_family_t af;
37 union {
38 struct in_addr v4;
39 struct in6_addr v6;
40 u_int8_t addr8[16];
41 u_int32_t addr32[4];
42 } xa; /* 128-bit address */
43 u_int32_t scope_id; /* iface scope id for v6 */
44#define v4 xa.v4
45#define v6 xa.v6
46#define addr8 xa.addr8
47#define addr32 xa.addr32
48};
49
50static int
51addr_unicast_masklen(int af)
52{
53 switch (af) {
54 case AF_INET:
55 return 32;
56 case AF_INET6:
57 return 128;
58 default:
59 return -1;
60 }
61}
62
63static inline int
64masklen_valid(int af, u_int masklen)
65{
66 switch (af) {
67 case AF_INET:
68 return masklen <= 32 ? 0 : -1;
69 case AF_INET6:
70 return masklen <= 128 ? 0 : -1;
71 default:
72 return -1;
73 }
74}
75
76/*
77 * Convert struct sockaddr to struct xaddr
78 * Returns 0 on success, -1 on failure.
79 */
80static int
81addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa)
82{
83 struct sockaddr_in *in4 = (struct sockaddr_in *)sa;
84 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
85
86 memset(xa, '\0', sizeof(*xa));
87
88 switch (sa->sa_family) {
89 case AF_INET:
90 if (slen < sizeof(*in4))
91 return -1;
92 xa->af = AF_INET;
93 memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4));
94 break;
95 case AF_INET6:
96 if (slen < sizeof(*in6))
97 return -1;
98 xa->af = AF_INET6;
99 memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6));
100 xa->scope_id = in6->sin6_scope_id;
101 break;
102 default:
103 return -1;
104 }
105
106 return 0;
107}
108
109/*
110 * Calculate a netmask of length 'l' for address family 'af' and
111 * store it in 'n'.
112 * Returns 0 on success, -1 on failure.
113 */
114static int
115addr_netmask(int af, u_int l, struct xaddr *n)
116{
117 int i;
118
119 if (masklen_valid(af, l) != 0 || n == NULL)
120 return -1;
121
122 memset(n, '\0', sizeof(*n));
123 switch (af) {
124 case AF_INET:
125 n->af = AF_INET;
126 n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff);
127 return 0;
128 case AF_INET6:
129 n->af = AF_INET6;
130 for (i = 0; i < 4 && l >= 32; i++, l -= 32)
131 n->addr32[i] = 0xffffffffU;
132 if (i < 4 && l != 0)
133 n->addr32[i] = htonl((0xffffffff << (32 - l)) &
134 0xffffffff);
135 return 0;
136 default:
137 return -1;
138 }
139}
140
141/*
142 * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'.
143 * Returns 0 on success, -1 on failure.
144 */
145static int
146addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b)
147{
148 int i;
149
150 if (dst == NULL || a == NULL || b == NULL || a->af != b->af)
151 return -1;
152
153 memcpy(dst, a, sizeof(*dst));
154 switch (a->af) {
155 case AF_INET:
156 dst->v4.s_addr &= b->v4.s_addr;
157 return 0;
158 case AF_INET6:
159 dst->scope_id = a->scope_id;
160 for (i = 0; i < 4; i++)
161 dst->addr32[i] &= b->addr32[i];
162 return 0;
163 default:
164 return -1;
165 }
166}
167
168/*
169 * Compare addresses 'a' and 'b'
170 * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b)
171 */
172static int
173addr_cmp(const struct xaddr *a, const struct xaddr *b)
174{
175 int i;
176
177 if (a->af != b->af)
178 return a->af == AF_INET6 ? 1 : -1;
179
180 switch (a->af) {
181 case AF_INET:
182 if (a->v4.s_addr == b->v4.s_addr)
183 return 0;
184 return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1;
185 case AF_INET6:
186 for (i = 0; i < 16; i++)
187 if (a->addr8[i] - b->addr8[i] != 0)
188 return a->addr8[i] > b->addr8[i] ? 1 : -1;
189 if (a->scope_id == b->scope_id)
190 return 0;
191 return a->scope_id > b->scope_id ? 1 : -1;
192 default:
193 return -1;
194 }
195}
196
197/*
198 * Parse string address 'p' into 'n'
199 * Returns 0 on success, -1 on failure.
200 */
201static int
202addr_pton(const char *p, struct xaddr *n)
203{
204 struct addrinfo hints, *ai;
205
206 memset(&hints, '\0', sizeof(hints));
207 hints.ai_flags = AI_NUMERICHOST;
208
209 if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0)
210 return -1;
211
212 if (ai == NULL || ai->ai_addr == NULL)
213 return -1;
214
215 if (n != NULL &&
216 addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) {
217 freeaddrinfo(ai);
218 return -1;
219 }
220
221 freeaddrinfo(ai);
222 return 0;
223}
224
225/*
226 * Perform bitwise negation of address
227 * Returns 0 on success, -1 on failure.
228 */
229static int
230addr_invert(struct xaddr *n)
231{
232 int i;
233
234 if (n == NULL)
235 return (-1);
236
237 switch (n->af) {
238 case AF_INET:
239 n->v4.s_addr = ~n->v4.s_addr;
240 return (0);
241 case AF_INET6:
242 for (i = 0; i < 4; i++)
243 n->addr32[i] = ~n->addr32[i];
244 return (0);
245 default:
246 return (-1);
247 }
248}
249
250/*
251 * Calculate a netmask of length 'l' for address family 'af' and
252 * store it in 'n'.
253 * Returns 0 on success, -1 on failure.
254 */
255static int
256addr_hostmask(int af, u_int l, struct xaddr *n)
257{
258 if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1)
259 return (-1);
260 return (0);
261}
262
263/*
264 * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::)
265 * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure.
266 */
267static int
268addr_is_all0s(const struct xaddr *a)
269{
270 int i;
271
272 switch (a->af) {
273 case AF_INET:
274 return (a->v4.s_addr == 0 ? 0 : -1);
275 case AF_INET6:;
276 for (i = 0; i < 4; i++)
277 if (a->addr32[i] != 0)
278 return (-1);
279 return (0);
280 default:
281 return (-1);
282 }
283}
284
285/*
286 * Test whether host portion of address 'a', as determined by 'masklen'
287 * is all zeros.
288 * Returns 0 on if host portion of address is all-zeros,
289 * -1 if not all zeros or on failure.
290 */
291static int
292addr_host_is_all0s(const struct xaddr *a, u_int masklen)
293{
294 struct xaddr tmp_addr, tmp_mask, tmp_result;
295
296 memcpy(&tmp_addr, a, sizeof(tmp_addr));
297 if (addr_hostmask(a->af, masklen, &tmp_mask) == -1)
298 return (-1);
299 if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1)
300 return (-1);
301 return (addr_is_all0s(&tmp_result));
302}
303
304/*
305 * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z).
306 * Return -1 on parse error, -2 on inconsistency or 0 on success.
307 */
308static int
309addr_pton_cidr(const char *p, struct xaddr *n, u_int *l)
310{
311 struct xaddr tmp;
312 long unsigned int masklen = 999;
313 char addrbuf[64], *mp, *cp;
314
315 /* Don't modify argument */
316 if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf))
317 return -1;
318
319 if ((mp = strchr(addrbuf, '/')) != NULL) {
320 *mp = '\0';
321 mp++;
322 masklen = strtoul(mp, &cp, 10);
323 if (*mp == '\0' || *cp != '\0' || masklen > 128)
324 return -1;
325 }
326
327 if (addr_pton(addrbuf, &tmp) == -1)
328 return -1;
329
330 if (mp == NULL)
331 masklen = addr_unicast_masklen(tmp.af);
332 if (masklen_valid(tmp.af, masklen) == -1)
333 return -2;
334 if (addr_host_is_all0s(&tmp, masklen) != 0)
335 return -2;
336
337 if (n != NULL)
338 memcpy(n, &tmp, sizeof(*n));
339 if (l != NULL)
340 *l = masklen;
341
342 return 0;
343}
344
345static int
346addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen)
347{
348 struct xaddr tmp_mask, tmp_result;
349
350 if (host->af != net->af)
351 return -1;
352
353 if (addr_netmask(host->af, masklen, &tmp_mask) == -1)
354 return -1;
355 if (addr_and(&tmp_result, host, &tmp_mask) == -1)
356 return -1;
357 return addr_cmp(&tmp_result, net);
358}
359
360/*
361 * Match "addr" against list pattern list "_list", which may contain a
362 * mix of CIDR addresses and old-school wildcards.
363 *
364 * If addr is NULL, then no matching is performed, but _list is parsed
365 * and checked for well-formedness.
366 *
367 * Returns 1 on match found (never returned when addr == NULL).
368 * Returns 0 on if no match found, or no errors found when addr == NULL.
369 * Returns -1 on negated match found (never returned when addr == NULL).
370 * Returns -2 on invalid list entry.
371 */
372int
373addr_match_list(const char *addr, const char *_list)
374{
375 char *list, *cp, *o;
376 struct xaddr try_addr, match_addr;
377 u_int masklen, neg;
378 int ret = 0, r;
379
380 if (addr != NULL && addr_pton(addr, &try_addr) != 0) {
381 debug2("%s: couldn't parse address %.100s", __func__, addr);
382 return 0;
383 }
384 if ((o = list = strdup(_list)) == NULL)
385 return -1;
386 while ((cp = strsep(&list, ",")) != NULL) {
387 neg = *cp == '!';
388 if (neg)
389 cp++;
390 if (*cp == '\0') {
391 ret = -2;
392 break;
393 }
394 /* Prefer CIDR address matching */
395 r = addr_pton_cidr(cp, &match_addr, &masklen);
396 if (r == -2) {
397 error("Inconsistent mask length for "
398 "network \"%.100s\"", cp);
399 ret = -2;
400 break;
401 } else if (r == 0) {
402 if (addr != NULL && addr_netmatch(&try_addr,
403 &match_addr, masklen) == 0) {
404 foundit:
405 if (neg) {
406 ret = -1;
407 break;
408 }
409 ret = 1;
410 }
411 continue;
412 } else {
413 /* If CIDR parse failed, try wildcard string match */
414 if (addr != NULL && match_pattern(addr, cp) == 1)
415 goto foundit;
416 }
417 }
418 free(o);
419
420 return ret;
421}