~ubuntu-branches/ubuntu/lucid/openssh/lucid

« back to all changes in this revision

Viewing changes to addrmatch.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2008-09-30 23:09:58 UTC
  • mfrom: (1.13.3 upstream) (29 hardy)
  • mto: This revision was merged to the branch mainline in revision 43.
  • Revision ID: james.westby@ubuntu.com-20080930230958-o6vsgn8c4mm959s0
Tags: 1:5.1p1-3
* Remove unnecessary ssh-vulnkey output in non-verbose mode when no
  compromised or unknown keys were found (closes: #496495).
* Configure with --disable-strip; dh_strip will deal with stripping
  binaries and will honour DEB_BUILD_OPTIONS (thanks, Bernhard R. Link;
  closes: #498681).
* Fix handling of zero-length server banners (thanks, Tomas Mraz; closes:
  #497026).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
35
struct 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
 
 
50
static int
 
51
addr_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
 
 
63
static inline int
 
64
masklen_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
 */
 
80
static int
 
81
addr_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
 */
 
114
static int
 
115
addr_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
 */
 
145
static int
 
146
addr_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
 */
 
172
static int
 
173
addr_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
 */
 
201
static int
 
202
addr_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
 */
 
229
static int
 
230
addr_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
 */
 
255
static int
 
256
addr_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
 */
 
267
static int
 
268
addr_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
 */
 
291
static int
 
292
addr_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
 */
 
308
static int
 
309
addr_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
 
 
345
static int
 
346
addr_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
 */
 
372
int
 
373
addr_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
}