diff options
Diffstat (limited to 'contrib/wpa/src/tls')
23 files changed, 3562 insertions, 277 deletions
diff --git a/contrib/wpa/src/tls/asn1.c b/contrib/wpa/src/tls/asn1.c index cec1092..822f87c 100644 --- a/contrib/wpa/src/tls/asn1.c +++ b/contrib/wpa/src/tls/asn1.c @@ -31,6 +31,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) pos = buf; end = buf + len; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier"); + return -1; + } hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); @@ -51,6 +55,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } else hdr->tag = hdr->identifier & 0x1f; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Length"); + return -1; + } tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { diff --git a/contrib/wpa/src/tls/asn1.h b/contrib/wpa/src/tls/asn1.h index 7475007..6bd7df5 100644 --- a/contrib/wpa/src/tls/asn1.h +++ b/contrib/wpa/src/tls/asn1.h @@ -20,6 +20,7 @@ #define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ #define ASN1_TAG_REAL 0x09 /* not yet parsed */ #define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_EMBEDDED_PDV 0x0B /* not yet parsed */ #define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ #define ANS1_TAG_RELATIVE_OID 0x0D #define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ @@ -35,7 +36,8 @@ #define ASN1_TAG_VISIBLESTRING 0x1A #define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ #define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ -#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_CHARACTERSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1E /* not yet parsed */ #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_APPLICATION 1 diff --git a/contrib/wpa/src/tls/bignum.c b/contrib/wpa/src/tls/bignum.c index f3baafe..1a87c82 100644 --- a/contrib/wpa/src/tls/bignum.c +++ b/contrib/wpa/src/tls/bignum.c @@ -119,10 +119,10 @@ int bignum_cmp(const struct bignum *a, const struct bignum *b) /** - * bignum_cmd_d - Compare bignum to standard integer + * bignum_cmp_d - Compare bignum to standard integer * @a: Bignum from bignum_init() * @b: Small integer - * Returns: 0 on success, -1 on failure + * Returns: -1 if a < b, 0 if a == b, 1 if a > b */ int bignum_cmp_d(const struct bignum *a, unsigned long b) { diff --git a/contrib/wpa/src/tls/libtommath.c b/contrib/wpa/src/tls/libtommath.c index 8bc824f..4f7a148 100644 --- a/contrib/wpa/src/tls/libtommath.c +++ b/contrib/wpa/src/tls/libtommath.c @@ -116,7 +116,7 @@ typedef int mp_err; #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ - #endif + #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ @@ -274,8 +274,8 @@ static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) *tmpc++ &= MP_MASK; } - /* now copy higher words if any, that is in A+B - * if A or B has more digits add those in + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in */ if (min != max) { for (; i < max; i++) { @@ -499,29 +499,29 @@ static int mp_mul (mp_int * a, mp_int * b, mp_int * c) #ifdef BN_MP_TOOM_MUL_C if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); - } else + } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul (a, b, c); - } else + } else #endif { /* can we use the fast multiplier? * - * The fast multiplier can be used if the output will - * have less than MP_WARRAY digits and the number of + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ #ifdef BN_FAST_S_MP_MUL_DIGS_C int digs = a->used + b->used + 1; if ((digs < MP_WARRAY) && - MIN(a->used, b->used) <= + MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { res = fast_s_mp_mul_digs (a, b, c, digs); - } else + } else #endif #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ @@ -629,7 +629,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; -#else +#else #error mp_exptmod would always fail /* no invmod */ return MP_VAL; @@ -658,7 +658,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) dr = mp_reduce_is_2k(P) << 1; } #endif - + /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if (mp_isodd (P) == 1 || dr != 0) { @@ -693,7 +693,7 @@ static int mp_cmp (mp_int * a, mp_int * b) return MP_GT; } } - + /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ @@ -779,7 +779,7 @@ static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) } /* init temps */ - if ((res = mp_init_multi(&x, &y, &u, &v, + if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } @@ -906,14 +906,14 @@ top: goto LBL_ERR; } } - + /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } - + /* C is now the inverse */ mp_exch (&C, c); res = MP_OKAY; @@ -933,7 +933,7 @@ static int mp_cmp_mag (mp_int * a, mp_int * b) if (a->used > b->used) { return MP_GT; } - + if (a->used < b->used) { return MP_LT; } @@ -1199,8 +1199,8 @@ static void mp_rshd (mp_int * a, int b) /* top [offset into digits] */ top = a->dp + b; - /* this is implemented as a sliding window where - * the window is b-digits long and digits from + /* this is implemented as a sliding window where + * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. @@ -1218,13 +1218,13 @@ static void mp_rshd (mp_int * a, int b) *bottom++ = 0; } } - + /* remove excess digits */ a->used -= b; } -/* swap the elements of two integers, for cases where you can't simply swap the +/* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ static void mp_exch (mp_int * a, mp_int * b) @@ -1237,7 +1237,7 @@ static void mp_exch (mp_int * a, mp_int * b) } -/* trim unused digits +/* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero @@ -1298,7 +1298,7 @@ static int mp_grow (mp_int * a, int size) #ifdef BN_MP_ABS_C -/* b = |a| +/* b = |a| * * Simple function copies the input and fixes the sign to positive */ @@ -1434,7 +1434,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) /* set the carry to the carry bits of the current word */ r = rr; } - + /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; @@ -1446,7 +1446,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) #ifdef BN_MP_INIT_MULTI_C -static int mp_init_multi(mp_int *mp, ...) +static int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ @@ -1460,11 +1460,11 @@ static int mp_init_multi(mp_int *mp, ...) succeeded in init-ing, then return error. */ va_list clean_args; - + /* end the current list */ va_end(args); - - /* now start cleaning up */ + + /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n--) { @@ -1484,7 +1484,7 @@ static int mp_init_multi(mp_int *mp, ...) #ifdef BN_MP_CLEAR_MULTI_C -static void mp_clear_multi(mp_int *mp, ...) +static void mp_clear_multi(mp_int *mp, ...) { mp_int* next_mp = mp; va_list args; @@ -1558,7 +1558,7 @@ static int mp_count_bits (mp_int * a) /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; - + /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit) 0)) { @@ -1628,7 +1628,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } return res; } - + /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; @@ -1638,7 +1638,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || - ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; @@ -1675,17 +1675,17 @@ LBL_ERR: #else -/* integer signed division. +/* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * - * Note that the description in HAC is horribly - * incomplete. For example, it doesn't consider - * the case where digits are removed from 'x' in - * the inner loop. It also doesn't consider the + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * - * The overall algorithm is as described as + * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) @@ -1775,7 +1775,7 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) continue; } - /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); @@ -1789,10 +1789,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); } - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; */ q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; do { @@ -1843,10 +1843,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) } } - /* now q is the quotient and x is the remainder - * [which we have to normalize] + /* now q is the quotient and x is the remainder + * [which we have to normalize] */ - + /* get sign before writing to c */ x.sign = x.used == 0 ? MP_ZPOS : a->sign; @@ -1914,7 +1914,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { - return err; + return err; } /* now init the second half of the array */ @@ -1932,7 +1932,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red if ((err = mp_init (&mu)) != MP_OKAY) { goto LBL_M; } - + if (redmode == 0) { if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { goto LBL_MU; @@ -1943,22 +1943,22 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red goto LBL_MU; } redux = mp_reduce_2k_l; - } + } /* create M table * - * The M table contains powers of the base, + * The M table contains powers of the base, * e.g. M[x] = G**x mod P * - * The first half of the table is not + * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } - /* compute the value at M[1<<(winsize-1)] by squaring - * M[1] (winsize-1) times + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; @@ -1966,7 +1966,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red for (x = 0; x < (winsize - 1); x++) { /* square it */ - if ((err = mp_sqr (&M[1 << (winsize - 1)], + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } @@ -2117,18 +2117,18 @@ static int mp_sqr (mp_int * a, mp_int * b) if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ - } else + } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr (a, b); - } else + } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ - if ((a->used * 2 + 1) < MP_WARRAY && - a->used < + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { res = fast_s_mp_sqr (a, b); } else @@ -2145,7 +2145,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) { } -/* reduces a modulo n where n is of the form 2**p - d +/* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ @@ -2153,33 +2153,33 @@ static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; - + if ((res = mp_init(&q)) != MP_OKAY) { return res; } - - p = mp_count_bits(n); + + p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } - + /* q = q * d */ - if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } - + /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } - + if (mp_cmp_mag(a, n) != MP_LT) { s_mp_sub(a, n, a); goto top; } - + ERR: mp_clear(&q); return res; @@ -2191,26 +2191,26 @@ static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; - + if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } - + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } - + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } - + ERR: mp_clear(&tmp); return res; } -/* computes a = 2**b +/* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. @@ -2243,7 +2243,7 @@ static int mp_2expt (mp_int * a, int b) static int mp_reduce_setup (mp_int * a, mp_int * b) { int res; - + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } @@ -2251,7 +2251,7 @@ static int mp_reduce_setup (mp_int * a, mp_int * b) } -/* reduces x mod m, assumes 0 < x < m**2, mu is +/* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ @@ -2266,7 +2266,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q1 = x / b**(k-1) */ - mp_rshd (&q, um - 1); + mp_rshd (&q, um - 1); /* according to HAC this optimization is ok */ if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { @@ -2282,8 +2282,8 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } -#else - { +#else + { #error mp_reduce would always fail res = MP_VAL; goto CLEANUP; @@ -2292,7 +2292,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q3 = q2 / b**(k+1) */ - mp_rshd (&q, um + 1); + mp_rshd (&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { @@ -2326,7 +2326,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) goto CLEANUP; } } - + CLEANUP: mp_clear (&q); @@ -2335,7 +2335,7 @@ CLEANUP: /* multiplies |a| * |b| and only computes up to digs digits of result - * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) @@ -2349,7 +2349,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && - MIN (a->used, b->used) < + MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } @@ -2372,10 +2372,10 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; - + /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; - + /* an alias for the digits of b */ tmpy = b->dp; @@ -2409,15 +2409,15 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * - * This is the fast column-array [comba] multiplier. It is - * designed to compute the columns of the product first - * then handle the carries afterwards. This has the effect + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * - * This has been modified to produce a variable number of - * digits of output so if say only a half-product is required - * you don't have to compute the upper half (a feature + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. @@ -2441,7 +2441,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* clear the carry */ _W = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; @@ -2454,7 +2454,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) tmpx = a->dp + tx; tmpy = b->dp + ty; - /* this is the number of times the loop will iterrate, essentially + /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); @@ -2501,8 +2501,8 @@ static int mp_init_size (mp_int * a, int size) int x; /* pad size so there are always extra digits */ - size += (MP_PREC * 2) - (size % MP_PREC); - + size += (MP_PREC * 2) - (size % MP_PREC); + /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); if (a->dp == NULL) { @@ -2556,7 +2556,7 @@ static int s_mp_sqr (mp_int * a, mp_int * b) /* alias for where to store the results */ tmpt = t.dp + (2*ix + 1); - + for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); @@ -2863,24 +2863,24 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* alias for source */ tmpa = a->dp; - + /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { - - /* get what will be the *next* carry bit from the - * MSB of the current digit + + /* get what will be the *next* carry bit from the + * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); - + /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; - - /* copy the carry that would be from the source - * digit into the next iteration + + /* copy the carry that would be from the source + * digit into the next iteration */ r = rr; } @@ -2892,8 +2892,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) ++(b->used); } - /* now zero any excess digits on the destination - * that we didn't write to + /* now zero any excess digits on the destination + * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { @@ -3011,7 +3011,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int /* determine and setup reduction code */ if (redmode == 0) { -#ifdef BN_MP_MONTGOMERY_SETUP_C +#ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { goto LBL_M; @@ -3026,7 +3026,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if (((P->used * 2 + 1) < MP_WARRAY) && P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { redux = fast_mp_montgomery_reduce; - } else + } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C @@ -3077,7 +3077,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { goto LBL_RES; } -#else +#else err = MP_VAL; goto LBL_RES; #endif @@ -3245,10 +3245,10 @@ LBL_M: #ifdef BN_FAST_S_MP_SQR_C /* the jist of squaring... - * you do like mult except the offset of the tmpx [one that - * starts closer to zero] can't equal the offset of tmpy. + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with - * (ty-tx) so that it never happens. You double all those + * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. @@ -3270,7 +3270,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) /* number of output digits to produce */ W1 = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; @@ -3291,7 +3291,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) */ iy = MIN(a->used-tx, ty+1); - /* now for squaring tx can never equal ty + /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ diff --git a/contrib/wpa/src/tls/pkcs5.c b/contrib/wpa/src/tls/pkcs5.c index 8a93483..a2ad83b 100644 --- a/contrib/wpa/src/tls/pkcs5.c +++ b/contrib/wpa/src/tls/pkcs5.c @@ -1,6 +1,6 @@ /* * PKCS #5 (Password-based Encryption) - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/crypto.h" #include "crypto/md5.h" +#include "crypto/sha1.h" #include "asn1.h" #include "pkcs5.h" @@ -18,30 +19,261 @@ struct pkcs5_params { enum pkcs5_alg { PKCS5_ALG_UNKNOWN, - PKCS5_ALG_MD5_DES_CBC + PKCS5_ALG_MD5_DES_CBC, + PKCS5_ALG_PBES2, + PKCS5_ALG_SHA1_3DES_CBC, } alg; - u8 salt[8]; + u8 salt[64]; size_t salt_len; unsigned int iter_count; + enum pbes2_enc_alg { + PBES2_ENC_ALG_UNKNOWN, + PBES2_ENC_ALG_DES_EDE3_CBC, + } enc_alg; + u8 iv[8]; + size_t iv_len; }; +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 7 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == alg; +} + + +static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 6 && + oid_is_rsadsi(oid) && + oid->oid[4] == 3 /* encryptionAlgorithm */ && + oid->oid[5] == alg; +} + + +static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 8 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 1 /* pkcs-12PbeIds */ && + oid->oid[7] == alg; +} + + static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { - if (oid->len == 7 && - oid->oid[0] == 1 /* iso */ && - oid->oid[1] == 2 /* member-body */ && - oid->oid[2] == 840 /* us */ && - oid->oid[3] == 113549 /* rsadsi */ && - oid->oid[4] == 1 /* pkcs */ && - oid->oid[5] == 5 /* pkcs-5 */ && - oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */ return PKCS5_ALG_MD5_DES_CBC; - + if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */ + return PKCS5_ALG_SHA1_3DES_CBC; + if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */ + return PKCS5_ALG_PBES2; return PKCS5_ALG_UNKNOWN; } +static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos, + const u8 *enc_alg_end) +{ + struct asn1_hdr hdr; + const u8 *end, *kdf_end; + struct asn1_oid oid; + char obuf[80]; + + /* + * RFC 2898, Ch. A.4 + * + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} } + * + * PBES2-KDFs ALGORITHM-IDENTIFIER ::= + * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBES2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (keyDerivationFunc) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + kdf_end = end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s", + obuf); + if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, C. + * + * PBKDF2-params ::= SEQUENCE { + * salt CHOICE { + * specified OCTET STRING, + * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + * }, + * iterationCount INTEGER (1..MAX), + * keyLength INTEGER (1..MAX) OPTIONAL, + * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT + * algid-hmacWithSHA1 + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (PBKDF2-params) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* For now, only support the salt CHOICE specified (OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length > sizeof(params->salt)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (salt.specified) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected INTEGER - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length == 1) { + params->iter_count = *hdr.payload; + } else if (hdr.length == 2) { + params->iter_count = WPA_GET_BE16(hdr.payload); + } else if (hdr.length == 4) { + params->iter_count = WPA_GET_BE32(hdr.payload); + } else { + wpa_hexdump(MSG_DEBUG, + "PKCS #5: Unsupported INTEGER value (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x", + params->iter_count); + return -1; + } + + /* For now, ignore optional keyLength and prf */ + + pos = kdf_end; + + /* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected SEQUENCE (encryptionScheme) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (encryptionScheme algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s", + obuf); + if (enc_alg_is_oid(&oid, 7)) { + params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC; + } else { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, B.2.2: + * The parameters field associated with this OID in an + * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)), + * specifying the initialization vector for CBC mode. + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length != 8) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Expected OCTET STRING (SIZE(8)) (IV) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + os_memcpy(params->iv, hdr.payload, hdr.length); + params->iv_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len); + + return 0; +} + + static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, struct pkcs5_params *params) { @@ -71,11 +303,23 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, return -1; } + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_get_params_pbes2(params, pos, enc_alg_end); + + /* PBES1 */ + /* * PKCS#5, Section 8 * PBEParameter ::= SEQUENCE { * salt OCTET STRING SIZE(8), * iterationCount INTEGER } + * + * Note: The same implementation can be used to parse the PKCS #12 + * version described in RFC 7292, C: + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } */ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || @@ -89,11 +333,11 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, pos = hdr.payload; end = hdr.payload + hdr.length; - /* salt OCTET STRING SIZE(8) */ + /* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OCTETSTRING || - hdr.length != 8) { + hdr.length > sizeof(params->salt)) { wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " "(salt) - found class %d tag 0x%x size %d", hdr.class, hdr.tag, hdr.length); @@ -136,6 +380,174 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, } +static struct crypto_cipher * +pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd) +{ + u8 key[24]; + + if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC || + params->iv_len != 8) + return NULL; + + wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2", + passwd, os_strlen(passwd)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2", + params->salt, params->salt_len); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u", + params->iter_count); + if (pbkdf2_sha1(passwd, params->salt, params->salt_len, + params->iter_count, key, sizeof(key)) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv, + key, sizeof(key)); +} + + +static void add_byte_array_mod(u8 *a, const u8 *b, size_t len) +{ + size_t i; + unsigned int carry = 0; + + for (i = len - 1; i < len; i--) { + carry = carry + a[i] + b[i]; + a[i] = carry & 0xff; + carry >>= 8; + } +} + + +static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt, + size_t salt_len, u8 id, unsigned int iter, + size_t out_len, u8 *out) +{ + unsigned int u, v, S_len, P_len, i; + u8 *D = NULL, *I = NULL, *B = NULL, *pos; + int res = -1; + + /* RFC 7292, B.2 */ + u = SHA1_MAC_LEN; + v = 64; + + /* D = copies of ID */ + D = os_malloc(v); + if (!D) + goto done; + os_memset(D, id, v); + + /* S = copies of salt; P = copies of password, I = S || P */ + S_len = v * ((salt_len + v - 1) / v); + P_len = v * ((pw_len + v - 1) / v); + I = os_malloc(S_len + P_len); + if (!I) + goto done; + pos = I; + if (salt_len) { + for (i = 0; i < S_len; i++) + *pos++ = salt[i % salt_len]; + } + if (pw_len) { + for (i = 0; i < P_len; i++) + *pos++ = pw[i % pw_len]; + } + + B = os_malloc(v); + if (!B) + goto done; + + for (;;) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = D; + len[0] = v; + addr[1] = I; + len[1] = S_len + P_len; + if (sha1_vector(2, addr, len, hash) < 0) + goto done; + + addr[0] = hash; + len[0] = SHA1_MAC_LEN; + for (i = 1; i < iter; i++) { + if (sha1_vector(1, addr, len, hash) < 0) + goto done; + } + + if (out_len <= u) { + os_memcpy(out, hash, out_len); + res = 0; + goto done; + } + + os_memcpy(out, hash, u); + out += u; + out_len -= u; + + /* I_j = (I_j + B + 1) mod 2^(v*8) */ + /* B = copies of Ai (final hash value) */ + for (i = 0; i < v; i++) + B[i] = hash[i % u]; + inc_byte_array(B, v); + for (i = 0; i < S_len + P_len; i += v) + add_byte_array_mod(&I[i], B, v); + } + +done: + os_free(B); + os_free(I); + os_free(D); + return res; +} + + +#define PKCS12_ID_ENC 1 +#define PKCS12_ID_IV 2 +#define PKCS12_ID_MAC 3 + +static struct crypto_cipher * +pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd) +{ + unsigned int i; + u8 *pw; + size_t pw_len; + u8 key[24]; + u8 iv[8]; + + if (params->alg != PKCS5_ALG_SHA1_3DES_CBC) + return NULL; + + pw_len = passwd ? os_strlen(passwd) : 0; + pw = os_malloc(2 * (pw_len + 1)); + if (!pw) + return NULL; + if (pw_len) { + for (i = 0; i <= pw_len; i++) + WPA_PUT_BE16(&pw[2 * i], passwd[i]); + pw_len = 2 * (pw_len + 1); + } + + if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_ENC, params->iter_count, + sizeof(key), key) < 0 || + pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_IV, params->iter_count, + sizeof(iv), iv) < 0) { + os_free(pw); + return NULL; + } + + os_free(pw); + + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key)); + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv)); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key)); +} + + static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const char *passwd) { @@ -144,6 +556,12 @@ static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const u8 *addr[2]; size_t len[2]; + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_crypto_init_pbes2(params, passwd); + + if (params->alg == PKCS5_ALG_SHA1_3DES_CBC) + return pkcs12_crypto_init_sha1(params, passwd); + if (params->alg != PKCS5_ALG_MD5_DES_CBC) return NULL; diff --git a/contrib/wpa/src/tls/rsa.c b/contrib/wpa/src/tls/rsa.c index 0b7b530..3525eb9 100644 --- a/contrib/wpa/src/tls/rsa.c +++ b/contrib/wpa/src/tls/rsa.c @@ -80,7 +80,7 @@ crypto_rsa_import_public_key(const u8 *buf, size_t len) * PKCS #1, 7.1: * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n - * publicExponent INTEGER -- e + * publicExponent INTEGER -- e * } */ diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index a6f0587..a147a54 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/src/tls/tlsv1_client.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/sha1.h" #include "crypto/tls.h" +#include "x509v3.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -110,7 +111,6 @@ int tls_derive_keys(struct tlsv1_client *conn, pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed @@ -264,7 +264,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to @@ -494,6 +494,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); wpabuf_free(conn->partial_input); + x509_certificate_chain_free(conn->server_cert); os_free(conn); } @@ -513,6 +514,8 @@ int tlsv1_client_established(struct tlsv1_client *conn) * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -520,13 +523,26 @@ int tlsv1_client_established(struct tlsv1_client *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -537,9 +553,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -691,18 +716,16 @@ int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, if (data == NULL || data_len == 0) return 0; - pos = conn->client_hello_ext = os_malloc(6 + data_len); + pos = conn->client_hello_ext = os_malloc(4 + data_len); if (pos == NULL) return -1; - WPA_PUT_BE16(pos, 4 + data_len); - pos += 2; WPA_PUT_BE16(pos, ext_type); pos += 2; WPA_PUT_BE16(pos, data_len); pos += 2; os_memcpy(pos, data, data_len); - conn->client_hello_ext_len = 6 + data_len; + conn->client_hello_ext_len = 4 + data_len; if (ext_type == TLS_EXT_PAC_OPAQUE) { conn->session_ticket_included = 1; @@ -813,9 +836,14 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +/** + * tlsv1_client_set_flags - Set connection flags + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @flags: TLS_CONN_* bitfield + */ +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags) { - conn->disable_time_checks = !enabled; + conn->flags = flags; } @@ -828,3 +856,38 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb) +{ + conn->event_cb = event_cb; + conn->cb_ctx = cb_ctx; + conn->cert_in_cb = !!cert_in_cb; +} + + +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + if (!conn) + return -1; + switch (conn->rl.tls_version) { + case TLS_VERSION_1: + os_strlcpy(buf, "TLSv1", buflen); + break; + case TLS_VERSION_1_1: + os_strlcpy(buf, "TLSv1.1", buflen); + break; + case TLS_VERSION_1_2: + os_strlcpy(buf, "TLSv1.2", buflen); + break; + default: + return -1; + } + + return 0; +} diff --git a/contrib/wpa/src/tls/tlsv1_client.h b/contrib/wpa/src/tls/tlsv1_client.h index a4e25e9..7fcc256 100644 --- a/contrib/wpa/src/tls/tlsv1_client.h +++ b/contrib/wpa/src/tls/tlsv1_client.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, @@ -41,7 +42,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, @@ -51,4 +52,12 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx); +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb); +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen); + #endif /* TLSV1_CLIENT_H */ diff --git a/contrib/wpa/src/tls/tlsv1_client_i.h b/contrib/wpa/src/tls/tlsv1_client_i.h index 55fdcf8..12ec8df 100644 --- a/contrib/wpa/src/tls/tlsv1_client_i.h +++ b/contrib/wpa/src/tls/tlsv1_client_i.h @@ -29,11 +29,14 @@ struct tlsv1_client { u8 alert_level; u8 alert_description; + unsigned int flags; /* TLS_CONN_* bitfield */ + unsigned int certificate_requested:1; unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; - unsigned int disable_time_checks:1; + unsigned int cert_in_cb:1; + unsigned int ocsp_resp_received:1; struct crypto_public_key *server_rsa_key; @@ -64,6 +67,12 @@ struct tlsv1_client { void *session_ticket_cb_ctx; struct wpabuf *partial_input; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + + struct x509_certificate *server_cert; }; @@ -81,4 +90,11 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len); +enum tls_ocsp_result { + TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED +}; + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len); + #endif /* TLSV1_CLIENT_I_H */ diff --git a/contrib/wpa/src/tls/tlsv1_client_ocsp.c b/contrib/wpa/src/tls/tlsv1_client_ocsp.c new file mode 100644 index 0000000..1d7b68c --- /dev/null +++ b/contrib/wpa/src/tls/tlsv1_client_ocsp.c @@ -0,0 +1,803 @@ +/* + * TLSv1 client - OCSP + * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "crypto/sha1.h" +#include "asn1.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */ +enum ocsp_response_status { + OCSP_RESP_STATUS_SUCCESSFUL = 0, + OCSP_RESP_STATUS_MALFORMED_REQ = 1, + OCSP_RESP_STATUS_INT_ERROR = 2, + OCSP_RESP_STATUS_TRY_LATER = 3, + /* 4 not used */ + OCSP_RESP_STATUS_SIG_REQUIRED = 5, + OCSP_RESP_STATUS_UNAUTHORIZED = 6, +}; + + +static int is_oid_basic_ocsp_resp(struct asn1_oid *oid) +{ + return oid->len == 10 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */ && + oid->oid[7] == 48 /* id-ad */ && + oid->oid[8] == 1 /* id-pkix-ocsp */ && + oid->oid[9] == 1 /* id-pkix-ocsp-basic */; +} + + +static int ocsp_responder_id_match(struct x509_certificate *signer, + struct x509_name *name, const u8 *key_hash) +{ + if (key_hash) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1] = { signer->public_key }; + size_t len[1] = { signer->public_key_len }; + + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0; + } + + return x509_name_compare(&signer->subject, name) == 0; +} + + +static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data, + size_t data_len, u8 *hash) +{ + const u8 *addr[1] = { data }; + size_t len[1] = { data_len }; + char buf[100]; + + if (x509_sha1_oid(alg)) { + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20); + return 20; + } + + if (x509_sha256_oid(alg)) { + if (sha256_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32); + return 32; + } + + if (x509_sha384_oid(alg)) { + if (sha384_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48); + return 48; + } + + if (x509_sha512_oid(alg)) { + if (sha512_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64); + return 64; + } + + + asn1_oid_to_str(alg, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s", + buf); + return 0; +} + + +static int tls_process_ocsp_single_response(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, + const u8 *resp, size_t len, + enum tls_ocsp_result *res) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct x509_algorithm_identifier alg; + const u8 *name_hash, *key_hash; + size_t name_hash_len, key_hash_len; + const u8 *serial_number; + size_t serial_number_len; + u8 hash[64]; + unsigned int hash_len; + unsigned int cert_status; + os_time_t update; + struct os_time now; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len); + + /* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + + /* CertID ::= SEQUENCE */ + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (CertID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, + * issuerKeyHash OCTET STRING, + * serialNumber CertificateSerialNumber } + */ + + /* hashAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return -1; + + /* issuerNameHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerNameHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + name_hash = hdr.payload; + name_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash", + name_hash, name_hash_len); + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN", + issuer->subject_dn, issuer->subject_dn_len); + hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn, + issuer->subject_dn_len, hash); + if (hash_len == 0 || name_hash_len != hash_len || + os_memcmp(name_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash", + hash, hash_len); + return -1; + } + + /* issuerKeyHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (issuerKeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + key_hash = hdr.payload; + key_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len); + pos = hdr.payload + hdr.length; + + hash_len = ocsp_hash_data(&alg.oid, issuer->public_key, + issuer->public_key_len, hash); + if (hash_len == 0 || key_hash_len != hash_len || + os_memcmp(key_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash", + hash, hash_len); + return -1; + } + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { + wpa_printf(MSG_DEBUG, "OCSP: No INTEGER tag found for serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); + return -1; + } + serial_number = hdr.payload; + serial_number_len = hdr.length; + while (serial_number_len > 0 && serial_number[0] == 0) { + serial_number++; + serial_number_len--; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number, + serial_number_len); + + if (serial_number_len != cert->serial_number_len || + os_memcmp(serial_number, cert->serial_number, + serial_number_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch"); + return -1; + } + + pos = end; + end = resp + len; + + /* certStatus CertStatus ::= CHOICE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (CertStatus) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + cert_status = hdr.tag; + wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status); + wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data", + hdr.payload, hdr.length); + pos = hdr.payload + hdr.length; + + os_get_time(&now); + /* thisUpdate GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update); + pos = hdr.payload + hdr.length; + if ((unsigned long) now.sec < (unsigned long) update) { + wpa_printf(MSG_DEBUG, + "OCSP: thisUpdate time in the future (response not yet valid)"); + return -1; + } + + /* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && hdr.tag == 0) { + const u8 *next = hdr.payload + hdr.length; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &update) < 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse nextUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu", + (unsigned long) update); + pos = next; + if ((unsigned long) now.sec > (unsigned long) update) { + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)"); + return -1; + } + } + } + + /* singleExtensions [1] EXPLICIT Extensions OPTIONAL */ + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions", + pos, end - pos); + /* Ignore for now */ + } + + if (cert_status == 0 /* good */) + *res = TLS_OCSP_GOOD; + else if (cert_status == 1 /* revoked */) + *res = TLS_OCSP_REVOKED; + else + return -1; + return 0; +} + + +static enum tls_ocsp_result +tls_process_ocsp_responses(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, const u8 *resp, + size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + enum tls_ocsp_result res; + + pos = resp; + end = resp + len; + while (pos < end) { + /* SingleResponse ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (SingleResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (tls_process_ocsp_single_response(conn, cert, issuer, + hdr.payload, hdr.length, + &res) == 0) + return res; + pos = hdr.payload + hdr.length; + } + + wpa_printf(MSG_DEBUG, + "OCSP: Did not find a response matching the server certificate"); + return TLS_OCSP_NO_RESPONSE; +} + + +static enum tls_ocsp_result +tls_process_basic_ocsp_response(struct tlsv1_client *conn, + struct x509_certificate *srv_cert, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + const u8 *resp_data, *sign_value, *key_hash = NULL, *responses; + const u8 *resp_data_signed; + size_t resp_data_len, sign_value_len, responses_len; + size_t resp_data_signed_len; + struct x509_algorithm_identifier alg; + struct x509_certificate *certs = NULL, *last_cert = NULL; + struct x509_certificate *issuer, *signer; + struct x509_name name; /* used if key_hash == NULL */ + char buf[100]; + os_time_t produced_at; + enum tls_ocsp_result res; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len); + + os_memset(&name, 0, sizeof(name)); + + /* + * RFC 6960, 4.2.1: + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (BasicOCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* ResponseData ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + resp_data = hdr.payload; + resp_data_len = hdr.length; + resp_data_signed = pos; + pos = hdr.payload + hdr.length; + resp_data_signed_len = pos - resp_data_signed; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return TLS_OCSP_INVALID; + + /* signature BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected BITSTRING (signature) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + if (hdr.length < 1) + return TLS_OCSP_INVALID; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "OCSP: BITSTRING - %d unused bits", *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + return TLS_OCSP_INVALID; + } + sign_value = pos + 1; + sign_value_len = hdr.length - 1; + pos += hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len); + + /* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (certs) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: certs", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + struct x509_certificate *cert; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) + goto fail; + if (last_cert) { + last_cert->next = cert; + last_cert = cert; + } else { + last_cert = certs = cert; + } + pos = hdr.payload + hdr.length; + } + } + + /* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + pos = resp_data; + end = resp_data + resp_data_len; + wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos); + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 && + hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && + hdr.tag == 0) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: No INTEGER (len=1) tag found for version field - found class %d tag 0x%x length %d", + hdr.class, hdr.tag, hdr.length); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u", + hdr.payload[0]); + if (hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Unsupported ResponseData version %u", + hdr.payload[0]); + goto no_resp; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, + "OCSP: Default ResponseData version (v1)"); + } + + /* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected CHOICE (ResponderID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + + if (hdr.tag == 1) { + /* Name */ + if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0) + goto fail; + x509_name_string(&name, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf); + } else if (hdr.tag == 2) { + /* KeyHash ::= OCTET STRING */ + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (KeyHash) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + key_hash = hdr.payload; + wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash", + key_hash, hdr.length); + if (hdr.length != SHA1_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1", + hdr.length, SHA1_MAC_LEN); + goto fail; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u", + hdr.tag); + goto fail; + } + + /* producedAt GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_GENERALIZEDTIME || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &produced_at) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt"); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu", + (unsigned long) produced_at); + pos = hdr.payload + hdr.length; + + /* responses SEQUENCE OF SingleResponse */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (responses) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto fail; + } + responses = hdr.payload; + responses_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* responseExtensions [1] EXPLICIT Extensions OPTIONAL */ + wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions", + pos, end - pos); + /* Ignore for now. */ + } + + if (!srv_cert) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate not known - cannot check OCSP response"); + goto no_resp; + } + + if (srv_cert->next) { + /* Issuer has already been verified in the chain */ + issuer = srv_cert->next; + } else { + /* Find issuer from the set of trusted certificates */ + for (issuer = conn->cred ? conn->cred->trusted_certs : NULL; + issuer; issuer = issuer->next) { + if (x509_name_compare(&srv_cert->issuer, + &issuer->subject) == 0) + break; + } + } + if (!issuer) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer not known - cannot check OCSP response"); + goto no_resp; + } + + if (ocsp_responder_id_match(issuer, &name, key_hash)) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer certificate matches ResponderID"); + signer = issuer; + } else { + for (signer = certs; signer; signer = signer->next) { + if (!ocsp_responder_id_match(signer, &name, key_hash) || + x509_name_compare(&srv_cert->issuer, + &issuer->subject) != 0 || + !(signer->ext_key_usage & + X509_EXT_KEY_USAGE_OCSP) || + x509_certificate_check_signature(issuer, signer) < + 0) + continue; + wpa_printf(MSG_DEBUG, + "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer"); + break; + } + if (!signer) { + wpa_printf(MSG_DEBUG, + "OCSP: Could not find OCSP signer certificate"); + goto no_resp; + } + } + + x509_free_name(&name); + os_memset(&name, 0, sizeof(name)); + x509_certificate_chain_free(certs); + certs = NULL; + + if (x509_check_signature(signer, &alg, sign_value, sign_value_len, + resp_data_signed, resp_data_signed_len) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Invalid signature"); + return TLS_OCSP_INVALID; + } + + res = tls_process_ocsp_responses(conn, srv_cert, issuer, + responses, responses_len); + if (res == TLS_OCSP_REVOKED) + srv_cert->ocsp_revoked = 1; + else if (res == TLS_OCSP_GOOD) + srv_cert->ocsp_good = 1; + return res; + +no_resp: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_NO_RESPONSE; + +fail: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_INVALID; +} + + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + u8 resp_status; + struct asn1_oid oid; + char obuf[80]; + struct x509_certificate *cert; + enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE; + enum tls_ocsp_result res_first = res; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len); + + /* + * RFC 6960, 4.2.1: + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (OCSPResponse) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* OCSPResponseStatus ::= ENUMERATED */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_ENUMERATED || + hdr.length != 1) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected ENUMERATED (responseStatus) - found class %d tag 0x%x length %u", + hdr.class, hdr.tag, hdr.length); + return TLS_OCSP_INVALID; + } + resp_status = hdr.payload[0]; + wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status); + pos = hdr.payload + hdr.length; + if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) { + wpa_printf(MSG_DEBUG, "OCSP: No stapling result"); + return TLS_OCSP_NO_RESPONSE; + } + + /* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL */ + if (pos == end) + return TLS_OCSP_NO_RESPONSE; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected [0] EXPLICIT (responseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + /* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected SEQUENCE (ResponseBytes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* responseType OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse OID (responseType)"); + return TLS_OCSP_INVALID; + } + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf); + if (!is_oid_basic_ocsp_resp(&oid)) { + wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type"); + return TLS_OCSP_NO_RESPONSE; + } + + /* response OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "OCSP: Expected OCTET STRING (response) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return TLS_OCSP_INVALID; + } + + cert = conn->server_cert; + while (cert) { + if (!cert->ocsp_good && !cert->ocsp_revoked) { + char sbuf[128]; + + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, + "OCSP: Trying to find certificate status for %s", + sbuf); + + res = tls_process_basic_ocsp_response(conn, cert, + hdr.payload, + hdr.length); + if (cert == conn->server_cert) + res_first = res; + } + if (res == TLS_OCSP_REVOKED || cert->issuer_trusted) + break; + cert = cert->next; + } + return res == TLS_OCSP_REVOKED ? res : res_first; +} diff --git a/contrib/wpa/src/tls/tlsv1_client_read.c b/contrib/wpa/src/tls/tlsv1_client_read.c index 9ce9680..80874e5 100644 --- a/contrib/wpa/src/tls/tlsv1_client_read.c +++ b/contrib/wpa/src/tls/tlsv1_client_read.c @@ -1,6 +1,6 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,6 +27,54 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); +static int tls_version_disabled(struct tlsv1_client *conn, u16 ver) +{ + return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + ver == TLS_VERSION_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + ver == TLS_VERSION_1_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + ver == TLS_VERSION_1_2)); +} + + +static int tls_process_server_hello_extensions(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions", + pos, len); + while (pos < end) { + u16 ext, elen; + + if (end - pos < 4) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header"); + return -1; + } + + ext = WPA_GET_BE16(pos); + pos += 2; + elen = WPA_GET_BE16(pos); + pos += 2; + + if (elen > end - pos) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u", + ext); + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data", + pos, elen); + + pos += elen; + } + + return 0; +} + + static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -76,7 +124,8 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); - if (!tls_version_ok(tls_version)) { + if (!tls_version_ok(tls_version) || + tls_version_disabled(conn, tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -165,8 +214,24 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, } pos++; + if (end - pos >= 2) { + u16 ext_len; + + ext_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < ext_len) { + wpa_printf(MSG_INFO, + "TLSv1: Invalid ServerHello extension length: %u (left: %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + if (tls_process_server_hello_extensions(conn, pos, ext_len)) + goto decode_error; + pos += ext_len; + } + if (end != pos) { - /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; @@ -211,6 +276,73 @@ decode_error: } +static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert) +{ + union tls_event_data ev; + struct wpabuf *cert_buf = NULL; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + char subject[128]; + + if (!conn->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.peer_cert.cert = cert_buf; + } +#ifdef CONFIG_SHA256 + if (cert_buf) { + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert_buf); + len[0] = wpabuf_len(cert_buf); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + + conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); +} + + +static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert, + enum tls_fail_reason reason, + const char *reason_txt) +{ + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; + char subject[128]; + + if (!conn->event_cb || !cert) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = reason_txt; + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.cert_fail.cert = cert_buf; + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +} + + static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -354,6 +486,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } + tls_peer_cert_event(conn, idx, cert); + if (last == NULL) chain = cert; else @@ -364,31 +498,99 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, pos += cert_len; } - if (conn->cred && - x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason, conn->disable_time_checks) - < 0) { + if (conn->cred && conn->cred->server_cert_only && chain) { + u8 hash[SHA256_MAC_LEN]; + char buf[128]; + + wpa_printf(MSG_DEBUG, + "TLSv1: Validate server certificate hash"); + x509_name_string(&chain->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); + if (sha256_vector(1, &chain->cert_start, &chain->cert_len, + hash) < 0 || + os_memcmp(conn->cred->srv_cert_hash, hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Server certificate hash mismatch"); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", + hash, SHA256_MAC_LEN); + if (conn->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED; + ev.cert_fail.reason_txt = + "Server certificate mismatch"; + ev.cert_fail.subject = buf; + conn->event_cb(conn->cb_ctx, + TLS_CERT_CHAIN_FAILURE, &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } else if (conn->cred && conn->cred->cert_probe) { + wpa_printf(MSG_DEBUG, + "TLSv1: Reject server certificate on probe-only rune"); + if (conn->event_cb) { + union tls_event_data ev; + char buf[128]; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE; + ev.cert_fail.reason_txt = + "Server certificate chain probe"; + if (chain) { + x509_name_string(&chain->subject, buf, + sizeof(buf)); + ev.cert_fail.subject = buf; + } + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, + &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } else if (conn->cred && conn->cred->ca_cert_verify && + x509_certificate_chain_validate( + conn->cred->trusted_certs, chain, &reason, + !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "bad certificate"); break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_REVOKED, + "certificate revoked"); break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_EXPIRED, + "certificate has expired or is not yet valid"); break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_UNTRUSTED, + "unknown CA"); break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -399,7 +601,25 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } - x509_certificate_chain_free(chain); + if (conn->cred && !conn->cred->server_cert_only && chain && + (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) { + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "certificate not allowed for server authentication"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + x509_certificate_chain_free(conn->server_cert); + conn->server_cert = chain; + } else { + x509_certificate_chain_free(chain); + } *in_len = end - in_data; @@ -465,10 +685,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos, conn->dh_p_len); goto fail; } - conn->dh_p = os_malloc(conn->dh_p_len); + conn->dh_p = os_memdup(pos, conn->dh_p_len); if (conn->dh_p == NULL) goto fail; - os_memcpy(conn->dh_p, pos, conn->dh_p_len); pos += conn->dh_p_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", conn->dh_p, conn->dh_p_len); @@ -480,10 +699,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_g_len = val; - conn->dh_g = os_malloc(conn->dh_g_len); + conn->dh_g = os_memdup(pos, conn->dh_g_len); if (conn->dh_g == NULL) goto fail; - os_memcpy(conn->dh_g, pos, conn->dh_g_len); pos += conn->dh_g_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", conn->dh_g, conn->dh_g_len); @@ -497,17 +715,16 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_ys_len = val; - conn->dh_ys = os_malloc(conn->dh_ys_len); + conn->dh_ys = os_memdup(pos, conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; - os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); server_params_end = pos; if (key_exchange == TLS_KEY_X_DHE_RSA) { - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + u8 hash[64]; int hlen; if (conn->rl.tls_version == TLS_VERSION_1_2) { @@ -524,18 +741,21 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, */ if (end - pos < 2) goto fail; - if (pos[0] != TLS_HASH_ALG_SHA256 || + if ((pos[0] != TLS_HASH_ALG_SHA256 && + pos[0] != TLS_HASH_ALG_SHA384 && + pos[0] != TLS_HASH_ALG_SHA512) || pos[1] != TLS_SIGN_ALG_RSA) { wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", pos[0], pos[1]); goto fail; } - pos += 2; hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, pos[0], + conn->client_random, conn->server_random, server_params, server_params_end - server_params, hash); + pos += 2; #else /* CONFIG_TLSV12 */ goto fail; #endif /* CONFIG_TLSV12 */ @@ -567,6 +787,229 @@ fail: } +static enum tls_ocsp_result +tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + u32 ocsp_resp_len; + + /* opaque OCSPResponse<1..2^24-1>; */ + if (end - pos < 3) { + wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + ocsp_resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < ocsp_resp_len) { + wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + + return tls_process_ocsp_response(conn, pos, ocsp_resp_len); +} + + +static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type, status_type; + enum tls_ocsp_result res; + struct x509_certificate *cert; + int depth; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, + "TLSv1: Expected Handshake; received content type 0x%x", + ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, + "TLSv1: Too short CertificateStatus (left=%lu)", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, + "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) { + wpa_printf(MSG_DEBUG, + "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)", + type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus"); + + /* + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + */ + if (end - pos < 1) { + wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", + status_type); + + if (status_type == 1 /* ocsp */) { + res = tls_process_certificate_status_ocsp_response( + conn, pos, end - pos); + } else if (status_type == 2 /* ocsp_multi */) { + int good = 0, revoked = 0; + u32 resp_len; + + res = TLS_OCSP_NO_RESPONSE; + + /* + * opaque OCSPResponse<0..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList"); + res = TLS_OCSP_INVALID; + goto done; + } + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < resp_len) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList(len=%u)", + resp_len); + res = TLS_OCSP_INVALID; + goto done; + } + end = pos + resp_len; + + while (end - pos >= 3) { + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (resp_len > end - pos) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", + resp_len, (int) (end - pos)); + res = TLS_OCSP_INVALID; + break; + } + if (!resp_len) + continue; /* Skip an empty response */ + res = tls_process_certificate_status_ocsp_response( + conn, pos - 3, resp_len + 3); + if (res == TLS_OCSP_REVOKED) + revoked++; + else if (res == TLS_OCSP_GOOD) + good++; + pos += resp_len; + } + + if (revoked) + res = TLS_OCSP_REVOKED; + else if (good) + res = TLS_OCSP_GOOD; + } else { + wpa_printf(MSG_DEBUG, + "TLSv1: Ignore unsupported CertificateStatus"); + goto skip; + } + +done: + if (res == TLS_OCSP_REVOKED) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_CERTIFICATE_REVOKED); + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (cert->ocsp_revoked) { + tls_cert_chain_failure_event( + conn, depth, cert, TLS_FAIL_REVOKED, + "certificate revoked"); + } + } + return -1; + } + + if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + /* + * Verify that each certificate on the chain that is not part + * of the trusted certificates has a good status. If not, + * terminate handshake. + */ + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (!cert->ocsp_good) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + tls_cert_chain_failure_event( + conn, depth, cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + if (cert->issuer_trusted) + break; + } + } + + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + if (conn->server_cert) + tls_cert_chain_failure_event( + conn, 0, conn->server_cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + + conn->ocsp_resp_received = 1; + +skip: + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -608,6 +1051,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, end = pos + len; + if ((conn->flags & TLS_CONN_REQUEST_OCSP) && + type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) + return tls_process_certificate_status(conn, ct, in_data, + in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); @@ -617,7 +1064,9 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" - "CertificateRequest/ServerHelloDone)", type); + "CertificateRequest/ServerHelloDone%s)", type, + (conn->flags & TLS_CONN_REQUEST_OCSP) ? + "/CertificateStatus" : ""); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -771,6 +1220,15 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && + !conn->ocsp_resp_received) { + wpa_printf(MSG_INFO, + "TLSv1: No OCSP response received - reject handshake"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + return -1; + } + *in_len = end - in_data; conn->state = CLIENT_KEY_EXCHANGE; diff --git a/contrib/wpa/src/tls/tlsv1_client_write.c b/contrib/wpa/src/tls/tlsv1_client_write.c index d192f44..4a1147b 100644 --- a/contrib/wpa/src/tls/tlsv1_client_write.c +++ b/contrib/wpa/src/tls/tlsv1_client_write.c @@ -1,6 +1,6 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -47,11 +47,34 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; struct os_time now; size_t len, i; + u8 *ext_start; + u16 tls_version = TLS_VERSION; - wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + /* Pick the highest locally enabled TLS version */ +#ifdef CONFIG_TLSV12 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + tls_version == TLS_VERSION_1_2) + tls_version = TLS_VERSION_1_1; +#endif /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + tls_version == TLS_VERSION_1_1) + tls_version = TLS_VERSION_1; +#endif /* CONFIG_TLSV11 */ + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + tls_version == TLS_VERSION_1) { + wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)", + tls_version_str(tls_version)); *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " @@ -61,7 +84,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", conn->client_random, TLS_RANDOM_LEN); - len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; hello = os_malloc(len); if (hello == NULL) return NULL; @@ -81,7 +104,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); @@ -101,12 +124,124 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) *pos++ = 1; *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * Add signature_algorithms extension since we support only + * SHA256 (and not the default SHA1) with TLSv1.2. + */ + /* ExtensionsType extension_type = signature_algorithms(13) */ + WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 8); + pos += 2; + /* supported_signature_algorithms<2..2^16-2> length */ + WPA_PUT_BE16(pos, 6); + pos += 2; + /* supported_signature_algorithms */ + *pos++ = TLS_HASH_ALG_SHA512; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA384; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + if (conn->client_hello_ext) { os_memcpy(pos, conn->client_hello_ext, conn->client_hello_ext_len); pos += conn->client_hello_ext_len; } + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* + * RFC 6066, 8: + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * } request; + * } CertificateStatusRequest; + * + * enum { ocsp(1), (255) } CertificateStatusType; + */ + *pos++ = 1; /* status_type = ocsp(1) */ + + /* + * struct { + * ResponderID responder_id_list<0..2^16-1>; + * Extensions request_extensions; + * } OCSPStatusRequest; + * + * opaque ResponderID<1..2^16-1>; + * opaque Extensions<0..2^16-1>; + */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request_v2 extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 7); + pos += 2; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + /* certificate_status_req_list<1..2^16-1> */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* CertificateStatusRequestItemV2 */ + *pos++ = 2; /* status_type = ocsp_multi(2) */ + /* OCSPStatusRequest as shown above for v1 */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + } + + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -134,6 +269,11 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, struct x509_certificate *cert; pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; @@ -154,7 +294,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -265,9 +405,16 @@ static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); + if (end - *pos < 2) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; - if (*pos + dh_yc_len > end) { + if (dh_yc_len > (size_t) (end - *pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -789,6 +936,8 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " "successfully"); + if (!conn->session_resumed && conn->use_session_ticket) + conn->session_resumed = 1; conn->state = ESTABLISHED; return msg; diff --git a/contrib/wpa/src/tls/tlsv1_common.c b/contrib/wpa/src/tls/tlsv1_common.c index dabc12a..e178915 100644 --- a/contrib/wpa/src/tls/tlsv1_common.c +++ b/contrib/wpa/src/tls/tlsv1_common.c @@ -21,7 +21,7 @@ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA * Add support for commonly used cipher suites; don't bother with exportable * suites. - */ + */ static const struct tls_cipher_suite tls_cipher_suites[] = { { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, @@ -335,7 +335,7 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, #ifdef CONFIG_TLSV12 -int tlsv12_key_x_server_params_hash(u16 tls_version, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg, const u8 *client_random, const u8 *server_random, const u8 *server_params, @@ -343,14 +343,30 @@ int tlsv12_key_x_server_params_hash(u16 tls_version, { size_t hlen; struct crypto_hash *ctx; - - ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + enum crypto_hash_alg alg; + + switch (hash_alg) { + case TLS_HASH_ALG_SHA256: + alg = CRYPTO_HASH_ALG_SHA256; + hlen = SHA256_MAC_LEN; + break; + case TLS_HASH_ALG_SHA384: + alg = CRYPTO_HASH_ALG_SHA384; + hlen = 48; + break; + case TLS_HASH_ALG_SHA512: + alg = CRYPTO_HASH_ALG_SHA512; + hlen = 64; + break; + default: + return -1; + } + ctx = crypto_hash_init(alg, NULL, 0); if (ctx == NULL) return -1; crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN); crypto_hash_update(ctx, server_params, server_params_len); - hlen = SHA256_MAC_LEN; if (crypto_hash_finish(ctx, hash, &hlen) < 0) return -1; @@ -466,9 +482,24 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 48 && + os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384"); + decrypted = buf + 19; + buflen -= 19; + } else if (buflen >= 19 + 64 && + os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512"); decrypted = buf + 19; buflen -= 19; + } else { wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo"); os_free(buf); diff --git a/contrib/wpa/src/tls/tlsv1_common.h b/contrib/wpa/src/tls/tlsv1_common.h index 26e68af..e30b15a 100644 --- a/contrib/wpa/src/tls/tlsv1_common.h +++ b/contrib/wpa/src/tls/tlsv1_common.h @@ -169,6 +169,8 @@ enum { #define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ +#define TLS_EXT_STATUS_REQUEST_V2 17 /* RFC 6961 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ @@ -257,7 +259,8 @@ int tls_version_ok(u16 ver); const char * tls_version_str(u16 ver); int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random, +int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg, + const u8 *client_random, const u8 *server_random, const u8 *server_params, size_t server_params_len, u8 *hash); diff --git a/contrib/wpa/src/tls/tlsv1_cred.c b/contrib/wpa/src/tls/tlsv1_cred.c index 1ea6827..842e5dd 100644 --- a/contrib/wpa/src/tls/tlsv1_cred.c +++ b/contrib/wpa/src/tls/tlsv1_cred.c @@ -1,6 +1,6 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,9 @@ #include "common.h" #include "base64.h" #include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "pkcs5.h" +#include "pkcs8.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -33,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } @@ -190,6 +195,43 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + + if (cert && os_strncmp(cert, "probe://", 8) == 0) { + cred->cert_probe = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); + return 0; + } + + cred->ca_cert_verify = cert || cert_blob || path; + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; @@ -288,6 +330,735 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, } +#ifdef PKCS12_FUNCS + +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) +{ + return oid->len == 9 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 10 && + oid->oid[7] == 1 /* bagtypes */ && + oid->oid[8] == type; +} + + +static int is_oid_pkcs7(struct asn1_oid *oid) +{ + return oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 7 /* pkcs-7 */; +} + + +static int is_oid_pkcs7_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; +} + + +static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; +} + + +static int is_oid_pkcs9(struct asn1_oid *oid) +{ + return oid->len >= 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 9 /* pkcs-9 */; +} + + +static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 20; +} + + +static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 21; +} + + +static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) +{ + return oid->len == 8 && is_oid_pkcs9(oid) && + oid->oid[6] == 22 /* certTypes */ && + oid->oid[7] == 1 /* x509Certificate */; +} + + +static int pkcs12_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + /* TODO */ + return 0; +} + + +static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct crypto_private_key *key; + + /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ + key = pkcs8_enc_key_import(buf, len, passwd); + if (!key) + return -1; + + wpa_printf(MSG_DEBUG, + "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); + crypto_private_key_free(cred->key); + cred->key = key; + + return 0; +} + + +static int pkcs12_certbag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos, *end; + + /* + * CertBag ::= SEQUENCE { + * certId BAG-TYPE.&id ({CertTypes}), + * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (certId)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); + + if (!is_oid_pkcs9_x509_cert(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Ignored unsupported certificate type (certId %s)", + obuf); + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", + hdr.payload, hdr.length); + if (cred->cert) { + struct x509_certificate *cert; + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse x509Certificate"); + return 0; + } + x509_certificate_chain_free(cert); + + return 0; + } + return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); +} + + +static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.1: + * friendlyName ATTRIBUTE ::= { + * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) + * EQUALITY MATCHING RULE caseIgnoreMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-friendlyName + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BMPSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.2: + * localKeyId ATTRIBUTE ::= { + * WITH SYNTAX OCTET STRING + * EQUALITY MATCHING RULE octetStringMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-localKeyId + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr(const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + struct asn1_hdr hdr; + struct asn1_oid a_oid; + char obuf[80]; + + /* + * PKCS12Attribute ::= SEQUENCE { + * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), + * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) + * } + */ + + if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); + return -1; + } + + asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (is_oid_pkcs9_friendly_name(&a_oid)) + return pkcs12_parse_attr_friendly_name(pos, end); + if (is_oid_pkcs9_local_key_id(&a_oid)) + return pkcs12_parse_attr_local_key_id(pos, end); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); + return 0; +} + + +static int pkcs12_safebag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos = buf, *end = buf + len; + const u8 *value; + size_t value_len; + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); + + /* BAG-TYPE ::= TYPE-IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (BAG-TYPE)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + value = hdr.payload; + value_len = hdr.length; + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + /* PKCS12Attribute ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + } + + if (pkcs12_is_bagtype_oid(&oid, 1)) + return pkcs12_keybag(cred, value, value_len); + if (pkcs12_is_bagtype_oid(&oid, 2)) + return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); + if (pkcs12_is_bagtype_oid(&oid, 3)) + return pkcs12_certbag(cred, value, value_len); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); + return 0; +} + + +static int pkcs12_safecontents(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* SafeContents ::= SEQUENCE OF SafeBag */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}) + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + + return 0; +} + + +static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); + + return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); +} + + +static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char buf[80]; + const u8 *enc_alg; + u8 *data; + size_t enc_alg_len, data_len; + int res = -1; + + /* + * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + /* Version ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); + return -1; + } + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", + pos, end - pos); + + /* + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", + buf); + + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", + buf); + return 0; + } + + /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + /* EncryptedContent ::= OCTET STRING */ + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + wpa_hexdump_key(MSG_MSGDUMP, + "PKCS #12: Decrypted encryptedContent", + data, data_len); + res = pkcs12_safecontents(cred, data, data_len, passwd); + os_free(data); + } + + return res; +} + + +static int pkcs12_parse_content(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + const u8 *pos = buf; + const u8 *end = buf + len; + struct asn1_oid oid; + char txt[80]; + struct asn1_hdr hdr; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return 0; + } + + asn1_oid_to_str(&oid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return 0; + } + pos = hdr.payload; + + if (is_oid_pkcs7_data(&oid)) + return pkcs12_parse_content_data(cred, pos, end, passwd); + if (is_oid_pkcs7_enc_data(&oid)) + return pkcs12_parse_content_enc_data(cred, pos, end, passwd); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", + txt); + + return 0; +} + + +static int pkcs12_parse(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct asn1_oid oid; + char buf[80]; + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + */ + + if (asn1_get_next(key, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, + "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 3) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); + return -1; + } + pos = hdr.payload + hdr.length; + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", + buf); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || + hdr.tag != 0) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + /* + * AuthenticatedSafe ::= SEQUENCE OF ContentInfo + * -- Data if unencrypted + * -- EncryptedData if password-encrypted + * -- EnvelopedData if public key-encrypted + */ + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", + hdr.payload, hdr.length); + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (end > pos) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used", + hdr.class, hdr.tag); + return -1; + } + if (pkcs12_parse_content(cred, hdr.payload, hdr.length, + passwd) < 0) + return -1; + + pos = hdr.payload + hdr.length; + } + + return 0; +} + +#endif /* PKCS12_FUNCS */ + + static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { @@ -296,6 +1067,10 @@ static int tlsv1_set_key(struct tlsv1_credentials *cred, cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); +#ifdef PKCS12_FUNCS + if (!cred->key) + pkcs12_parse(cred, key, len, passwd); +#endif /* PKCS12_FUNCS */ if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; @@ -391,10 +1166,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_p); - cred->dh_p = os_malloc(hdr.length); + cred->dh_p = os_memdup(hdr.payload, hdr.length); if (cred->dh_p == NULL) return -1; - os_memcpy(cred->dh_p, hdr.payload, hdr.length); cred->dh_p_len = hdr.length; pos = hdr.payload + hdr.length; @@ -413,10 +1187,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_g); - cred->dh_g = os_malloc(hdr.length); + cred->dh_g = os_memdup(hdr.payload, hdr.length); if (cred->dh_g == NULL) return -1; - os_memcpy(cred->dh_g, hdr.payload, hdr.length); cred->dh_g_len = hdr.length; return 0; diff --git a/contrib/wpa/src/tls/tlsv1_cred.h b/contrib/wpa/src/tls/tlsv1_cred.h index 68fbdc9..716e93c 100644 --- a/contrib/wpa/src/tls/tlsv1_cred.h +++ b/contrib/wpa/src/tls/tlsv1_cred.h @@ -14,11 +14,19 @@ struct tlsv1_credentials { struct x509_certificate *cert; struct crypto_private_key *key; + unsigned int cert_probe:1; + unsigned int ca_cert_verify:1; + unsigned int server_cert_only:1; + u8 srv_cert_hash[32]; + /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */ size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; + + char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/contrib/wpa/src/tls/tlsv1_server.c b/contrib/wpa/src/tls/tlsv1_server.c index ba47337..12dcc85 100644 --- a/contrib/wpa/src/tls/tlsv1_server.c +++ b/contrib/wpa/src/tls/tlsv1_server.c @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -164,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); goto failed; } ct = pos[0]; @@ -204,6 +205,7 @@ failed: msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); + conn->write_alerts++; } return msg; @@ -216,7 +218,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to @@ -296,6 +298,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } tlsv1_server_log(conn, "Received alert %d:%d", out_pos[0], out_pos[1]); + conn->read_alerts++; if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -459,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn) * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -466,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -483,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -708,6 +735,24 @@ void tlsv1_server_set_log_cb(struct tlsv1_server *conn, } +int tlsv1_server_get_failed(struct tlsv1_server *conn) +{ + return conn->state == FAILED; +} + + +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn) +{ + return conn->read_alerts; +} + + +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn) +{ + return conn->write_alerts; +} + + #ifdef CONFIG_TESTING_OPTIONS void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) { diff --git a/contrib/wpa/src/tls/tlsv1_server.h b/contrib/wpa/src/tls/tlsv1_server.h index 10e7699..c9c0875 100644 --- a/contrib/wpa/src/tls/tlsv1_server.h +++ b/contrib/wpa/src/tls/tlsv1_server.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); @@ -48,6 +49,10 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, void tlsv1_server_set_log_cb(struct tlsv1_server *conn, void (*cb)(void *ctx, const char *msg), void *ctx); +int tlsv1_server_get_failed(struct tlsv1_server *conn); +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn); +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn); + void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); #endif /* TLSV1_SERVER_H */ diff --git a/contrib/wpa/src/tls/tlsv1_server_i.h b/contrib/wpa/src/tls/tlsv1_server_i.h index 96d79b3..2622585 100644 --- a/contrib/wpa/src/tls/tlsv1_server_i.h +++ b/contrib/wpa/src/tls/tlsv1_server_i.h @@ -30,6 +30,8 @@ struct tlsv1_server { u8 alert_level; u8 alert_description; + int read_alerts, write_alerts; + struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; @@ -55,6 +57,9 @@ struct tlsv1_server { void *log_cb_ctx; int use_session_ticket; + unsigned int status_request:1; + unsigned int status_request_v2:1; + unsigned int status_request_multi:1; u8 *dh_secret; size_t dh_secret_len; diff --git a/contrib/wpa/src/tls/tlsv1_server_read.c b/contrib/wpa/src/tls/tlsv1_server_read.c index 0f237ba..e957678 100644 --- a/contrib/wpa/src/tls/tlsv1_server_read.c +++ b/contrib/wpa/src/tls/tlsv1_server_read.c @@ -46,6 +46,78 @@ static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) } +static void tls_process_status_request_item(struct tlsv1_server *conn, + const u8 *req, size_t req_len) +{ + const u8 *pos, *end; + u8 status_type; + + pos = req; + end = req + req_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + */ + + if (end - pos < 1) + return; /* Truncated data */ + + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type); + if (status_type != 1 && status_type != 2) + return; /* Unsupported status type */ + /* + * For now, only OCSP stapling is supported, so ignore the specific + * request, if any. + */ + wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos); + + if (status_type == 2) + conn->status_request_multi = 1; +} + + +static void tls_process_status_request_v2(struct tlsv1_server *conn, + const u8 *ext, size_t ext_len) +{ + const u8 *pos, *end; + + conn->status_request_v2 = 1; + + pos = ext; + end = ext + ext_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + while (end - pos >= 2) { + u16 len; + + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) + break; /* Truncated data */ + tls_process_status_request_item(conn, pos, len); + pos += len; + } +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -67,8 +139,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos = in_data; left = *in_len; - if (left < 4) + if (left < 4) { + tlsv1_server_log(conn, + "Truncated handshake message (expected ClientHello)"); goto decode_error; + } /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { @@ -85,8 +160,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 3; left -= 4; - if (len > left) + if (len > left) { + tlsv1_server_log(conn, + "Truncated ClientHello (len=%d left=%d)", + (int) len, (int) left); goto decode_error; + } /* body - ClientHello */ @@ -94,8 +173,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, end = pos + len; /* ProtocolVersion client_version */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, "Truncated ClientHello/client_version"); goto decode_error; + } conn->client_version = WPA_GET_BE16(pos); tlsv1_server_log(conn, "Client version %d.%d", conn->client_version >> 8, @@ -124,8 +205,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, tls_version_str(conn->rl.tls_version)); /* Random random */ - if (end - pos < TLS_RANDOM_LEN) + if (end - pos < TLS_RANDOM_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/client_random"); goto decode_error; + } os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; @@ -133,25 +216,36 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id len"); goto decode_error; - if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + } + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, + "Truncated ClientHello/cipher_suites len"); goto decode_error; + } num_suites = WPA_GET_BE16(pos); pos += 2; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); - if (num_suites & 1) + if (num_suites & 1) { + tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites"); goto decode_error; + } num_suites /= 2; cipher_suite = 0; @@ -187,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods len"); goto decode_error; + } num_suites = *pos++; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; @@ -267,6 +367,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len); conn->session_ticket_len = ext_len; } + } else if (ext_type == TLS_EXT_STATUS_REQUEST) { + conn->status_request = 1; + } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) { + tls_process_status_request_v2(conn, pos, + ext_len); } pos += ext_len; @@ -471,6 +576,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, return -1; } + if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + x509_certificate_chain_free(chain); *in_len = end - in_data; @@ -1131,6 +1245,7 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { tlsv1_server_log(conn, "Mismatch in verify_data"); + conn->state = FAILED; return -1; } diff --git a/contrib/wpa/src/tls/tlsv1_server_write.c b/contrib/wpa/src/tls/tlsv1_server_write.c index 15e6692..8d36cf1 100644 --- a/contrib/wpa/src/tls/tlsv1_server_write.c +++ b/contrib/wpa/src/tls/tlsv1_server_write.c @@ -26,7 +26,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) size_t len = 0; struct x509_certificate *cert; - cert = conn->cred->cert; + cert = conn->cred ? conn->cred->cert : NULL; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) @@ -42,7 +42,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start; struct os_time now; size_t rlen; @@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " @@ -97,6 +100,32 @@ static int tls_write_server_hello(struct tlsv1_server *conn, /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + + if (conn->status_request) { + /* Add a status_request extension with empty extension_data */ + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + + if (conn->status_request_v2) { + /* + Add a status_request_v2 extension with empty extension_data + */ + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, @@ -133,6 +162,11 @@ static int tls_write_server_hello(struct tlsv1_server *conn, */ } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -168,6 +202,11 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; @@ -188,7 +227,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos += 3; cert = conn->cred->cert; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -239,6 +278,93 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } +static int tls_write_server_certificate_status(struct tlsv1_server *conn, + u8 **msgpos, u8 *end, + int ocsp_multi, + char *ocsp_resp, + size_t ocsp_resp_len) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!ocsp_resp) { + /* + * Client did not request certificate status or there is no + * matching response cached. + */ + return 0; + } + + pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len > + (unsigned int) (end - pos)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - CertificateStatus + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + * + * opaque OCSPResponse<1..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + + /* CertificateStatusType status_type */ + if (ocsp_multi) + *pos++ = 2; /* ocsp_multi(2) */ + else + *pos++ = 1; /* ocsp(1) */ + /* uint24 length of OCSPResponse */ + WPA_PUT_BE24(pos, ocsp_resp_len); + pos += 3; + os_memcpy(pos, ocsp_resp, ocsp_resp_len); + pos += ocsp_resp_len; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { @@ -371,7 +497,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, /* body - ServerDHParams */ server_params = pos; /* dh_p */ - if (pos + 2 + dh_p_len > end) { + if (2 + dh_p_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -385,7 +511,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_p_len; /* dh_g */ - if (pos + 2 + conn->cred->dh_g_len > end) { + if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_g"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -399,7 +525,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += conn->cred->dh_g_len; /* dh_Ys */ - if (pos + 2 + dh_ys_len > end) { + if (2 + dh_ys_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_Ys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -443,7 +569,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, if (conn->rl.tls_version >= TLS_VERSION_1_2) { #ifdef CONFIG_TLSV12 hlen = tlsv12_key_x_server_params_hash( - conn->rl.tls_version, conn->client_random, + conn->rl.tls_version, TLS_HASH_ALG_SHA256, + conn->client_random, conn->server_random, server_params, pos - server_params, hash + 19); @@ -457,7 +584,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; */ - if (hlen < 0 || pos + 2 > end) { + if (hlen < 0 || end - pos < 2) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); return -1; @@ -804,24 +931,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; + int ocsp_multi = 0; + char *ocsp_resp = NULL; + size_t ocsp_resp_len = 0; *out_len = 0; - msglen = 1000 + tls_server_cert_chain_der_len(conn); + if (conn->status_request_multi && + conn->cred->ocsp_stapling_response_multi) { + ocsp_resp = os_readfile( + conn->cred->ocsp_stapling_response_multi, + &ocsp_resp_len); + ocsp_multi = 1; + } else if ((conn->status_request || conn->status_request_v2) && + conn->cred->ocsp_stapling_response) { + ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response, + &ocsp_resp_len); + } + if (!ocsp_resp) + ocsp_resp_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len; msg = os_malloc(msglen); - if (msg == NULL) + if (msg == NULL) { + os_free(ocsp_resp); return NULL; + } pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } if (conn->use_session_ticket) { + os_free(ocsp_resp); + /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { @@ -838,12 +987,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_certificate_status(conn, &pos, end, ocsp_multi, + ocsp_resp, ocsp_resp_len) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } + os_free(ocsp_resp); *out_len = pos - msg; diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index b51dfcd..fa4d442 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -1,6 +1,6 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,7 +14,7 @@ #include "x509v3.h" -static void x509_free_name(struct x509_name *name) +void x509_free_name(struct x509_name *name) { size_t i; @@ -55,6 +55,7 @@ void x509_certificate_free(struct x509_certificate *cert) x509_free_name(&cert->subject); os_free(cert->public_key); os_free(cert->sign_value); + os_free(cert->subject_dn); os_free(cert); } @@ -177,9 +178,9 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b) } -static int x509_parse_algorithm_identifier( - const u8 *buf, size_t len, - struct x509_algorithm_identifier *id, const u8 **next) +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; @@ -199,12 +200,11 @@ static int x509_parse_algorithm_identifier( hdr.class, hdr.tag); return -1; } + if (hdr.length > buf + len - hdr.payload) + return -1; pos = hdr.payload; end = pos + hdr.length; - if (end > buf + len) - return -1; - *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) @@ -243,7 +243,7 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } pos = hdr.payload; - if (pos + hdr.length > end) + if (hdr.length > end - pos) return -1; end = pos + hdr.length; *next = end; @@ -274,13 +274,12 @@ static int x509_parse_public_key(const u8 *buf, size_t len, */ } os_free(cert->public_key); - cert->public_key = os_malloc(hdr.length - 1); + cert->public_key = os_memdup(pos + 1, hdr.length - 1); if (cert->public_key == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "public key"); return -1; } - os_memcpy(cert->public_key, pos + 1, hdr.length - 1); cert->public_key_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", cert->public_key, cert->public_key_len); @@ -289,8 +288,8 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } -static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, - const u8 **next) +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; @@ -319,7 +318,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, } pos = hdr.payload; - if (pos + hdr.length > buf + len) + if (hdr.length > buf + len - pos) return -1; end = *next = pos + hdr.length; @@ -533,12 +532,13 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) } done: + if (pos < end) + *pos = '\0'; end[-1] = '\0'; } -static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, - os_time_t *val) +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) { const char *pos; int year, month, day, hour, min, sec; @@ -677,7 +677,7 @@ static int x509_parse_validity(const u8 *buf, size_t len, pos = hdr.payload; plen = hdr.length; - if (pos + plen > buf + len) + if (plen > (size_t) (buf + len - pos)) return -1; *next = pos + plen; @@ -721,6 +721,15 @@ static int x509_id_ce_oid(struct asn1_oid *oid) } +static int x509_any_ext_key_usage_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + x509_id_ce_oid(oid) && + oid->oid[3] == 37 /* extKeyUsage */ && + oid->oid[4] == 0 /* anyExtendedKeyUsage */; +} + + static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { @@ -801,7 +810,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } cert->ca = hdr.payload[0]; - if (hdr.payload + hdr.length == pos + len) { + if (hdr.length == pos + len - hdr.payload) { wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; @@ -917,10 +926,9 @@ static int x509_parse_alt_name_ip(struct x509_name *name, /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); os_free(name->ip); - name->ip = os_malloc(len); + name->ip = os_memdup(pos, len); if (name->ip == NULL) return -1; - os_memcpy(name->ip, pos, len); name->ip_len = len; return 0; } @@ -1074,6 +1082,112 @@ static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, } +static int x509_id_pkix_oid(struct asn1_oid *oid) +{ + return oid->len >= 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */; +} + + +static int x509_id_kp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len >= 8 && + x509_id_pkix_oid(oid) && + oid->oid[7] == 3 /* id-kp */; +} + + +static int x509_id_kp_server_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 1 /* id-kp-serverAuth */; +} + + +static int x509_id_kp_client_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 2 /* id-kp-clientAuth */; +} + + +static int x509_id_kp_ocsp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 9 /* id-kp-OCSPSigning */; +} + + +static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *end; + struct asn1_oid oid; + + /* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(ExtKeyUsageSyntax) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length > pos + len - hdr.payload) + return -1; + pos = hdr.payload; + end = pos + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos); + + while (pos < end) { + char buf[80]; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) + return -1; + if (x509_any_ext_key_usage_oid(&oid)) { + os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY; + } else if (x509_id_kp_server_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH; + } else if (x509_id_kp_client_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH; + } else if (x509_id_kp_ocsp_oid(&oid)) { + os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP; + } else { + asn1_oid_to_str(&oid, buf, sizeof(buf)); + } + wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf); + } + + cert->extensions_present |= X509_EXT_EXT_KEY_USAGE; + + return 0; +} + + static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) @@ -1085,7 +1199,6 @@ static int x509_parse_extension_data(struct x509_certificate *cert, * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) - * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ switch (oid->oid[3]) { @@ -1097,6 +1210,8 @@ static int x509_parse_extension_data(struct x509_certificate *cert, return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); + case 37: /* id-ce-extKeyUsage */ + return x509_parse_ext_ext_key_usage(cert, pos, len); default: return 1; } @@ -1224,6 +1339,7 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, size_t left; char sbuf[128]; unsigned long value; + const u8 *subject_dn; /* tbsCertificate TBSCertificate ::= SEQUENCE */ if (asn1_get_next(buf, len, &hdr) < 0 || @@ -1287,21 +1403,23 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, /* serialNumber CertificateSerialNumber ::= INTEGER */ if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { + hdr.tag != ASN1_TAG_INTEGER || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " - "serialNumber; class=%d tag=0x%x", - hdr.class, hdr.tag); + "serialNumber; class=%d tag=0x%x length=%u", + hdr.class, hdr.tag, hdr.length); return -1; } - pos = hdr.payload; - left = hdr.length; - while (left) { - cert->serial_number <<= 8; - cert->serial_number |= *pos++; - left--; + pos = hdr.payload + hdr.length; + while (hdr.length > 0 && hdr.payload[0] == 0) { + hdr.payload++; + hdr.length--; } - wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); + os_memcpy(cert->serial_number, hdr.payload, hdr.length); + cert->serial_number_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number, + cert->serial_number_len); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, @@ -1319,8 +1437,14 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; /* subject Name */ + subject_dn = pos; if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; + cert->subject_dn = os_malloc(pos - subject_dn); + if (!cert->subject_dn) + return -1; + cert->subject_dn_len = pos - subject_dn; + os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len); x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); @@ -1437,7 +1561,7 @@ static int x509_digest_oid(struct asn1_oid *oid) } -static int x509_sha1_oid(struct asn1_oid *oid) +int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && @@ -1449,7 +1573,7 @@ static int x509_sha1_oid(struct asn1_oid *oid) } -static int x509_sha256_oid(struct asn1_oid *oid) +static int x509_sha2_oid(struct asn1_oid *oid) { return oid->len == 9 && oid->oid[0] == 2 /* joint-iso-itu-t */ && @@ -1459,11 +1583,31 @@ static int x509_sha256_oid(struct asn1_oid *oid) oid->oid[4] == 101 /* gov */ && oid->oid[5] == 3 /* csor */ && oid->oid[6] == 4 /* nistAlgorithm */ && - oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[7] == 2 /* hashAlgs */; +} + + +int x509_sha256_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && oid->oid[8] == 1 /* sha256 */; } +int x509_sha384_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 2 /* sha384 */; +} + + +int x509_sha512_oid(struct asn1_oid *oid) +{ + return x509_sha2_oid(oid) && + oid->oid[8] == 3 /* sha512 */; +} + + /** * x509_certificate_parse - Parse a X.509 certificate in DER format * @buf: Pointer to the X.509 certificate in DER format @@ -1503,12 +1647,12 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) } pos = hdr.payload; - if (pos + hdr.length > end) { + if (hdr.length > end - pos) { x509_certificate_free(cert); return NULL; } - if (pos + hdr.length < end) { + if (hdr.length < end - pos) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", pos + hdr.length, end - (pos + hdr.length)); @@ -1556,14 +1700,13 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) return NULL; } os_free(cert->sign_value); - cert->sign_value = os_malloc(hdr.length - 1); + cert->sign_value = os_memdup(pos + 1, hdr.length - 1); if (cert->sign_value == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "signatureValue"); x509_certificate_free(cert); return NULL; } - os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); @@ -1582,18 +1725,31 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) { + return x509_check_signature(issuer, &cert->signature, + cert->sign_value, cert->sign_value_len, + cert->tbs_cert_start, cert->tbs_cert_len); +} + + +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len) +{ struct crypto_public_key *pk; u8 *data; const u8 *pos, *end, *next, *da_end; size_t data_len; struct asn1_hdr hdr; struct asn1_oid oid; - u8 hash[32]; + u8 hash[64]; size_t hash_len; + const u8 *addr[1] = { signed_data }; + size_t len[1] = { signed_data_len }; - if (!x509_pkcs_oid(&cert->signature.oid) || - cert->signature.oid.len != 7 || - cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + if (!x509_pkcs_oid(&signature->oid) || + signature->oid.len != 7 || + signature->oid.oid[5] != 1 /* pkcs-1 */) { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; @@ -1604,15 +1760,15 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, if (pk == NULL) return -1; - data_len = cert->sign_value_len; + data_len = sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } - if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, - cert->sign_value_len, data, + if (crypto_public_key_decrypt_pkcs1(pk, sign_value, + sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); @@ -1675,12 +1831,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha1_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 5 /* sha-1WithRSAEncryption */) { + if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1688,12 +1843,36 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha256_oid(&oid)) { - if (cert->signature.oid.oid[6] != + if (signature->oid.oid[6] != 11 /* sha2561WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha384_oid(&oid)) { + if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha512_oid(&oid)) { + if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + signature->oid.oid[6]); os_free(data); return -1; } @@ -1707,12 +1886,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } switch (oid.oid[5]) { case 5: /* md5 */ - if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) - { + if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1743,34 +1921,41 @@ skip_digest_oid: wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); - switch (cert->signature.oid.oid[6]) { + switch (signature->oid.oid[6]) { case 4: /* md5WithRSAEncryption */ - md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + md5_vector(1, addr, len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: /* sha-1WithRSAEncryption */ - sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha1_vector(1, addr, len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ - sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha256_vector(1, addr, len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; - case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ + sha384_vector(1, addr, len, hash); + hash_len = 48; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)", + hash, hash_len); + break; case 13: /* sha512WithRSAEncryption */ + sha512_vector(1, addr, len, hash); + hash_len = 64; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)", + hash, hash_len); + break; + case 2: /* md2WithRSAEncryption */ default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " - "algorithm (%lu)", cert->signature.oid.oid[6]); + "algorithm (%lu)", signature->oid.oid[6]); os_free(data); return -1; } @@ -1852,7 +2037,8 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { - x509_name_string(&cert->subject, buf, sizeof(buf)); + cert->issuer_trusted = 0; + x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); if (chain_trusted) @@ -1876,11 +2062,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Certificate " "chain issuer name mismatch"); x509_name_string(&cert->issuer, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", buf); x509_name_string(&cert->next->subject, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: next cert " "subject: %s", buf); *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; @@ -1937,6 +2123,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); + cert->issuer_trusted = 1; chain_trusted = 1; } } diff --git a/contrib/wpa/src/tls/x509v3.h b/contrib/wpa/src/tls/x509v3.h index 91a35ba..7df8e2a 100644 --- a/contrib/wpa/src/tls/x509v3.h +++ b/contrib/wpa/src/tls/x509v3.h @@ -45,13 +45,18 @@ struct x509_name { struct asn1_oid rid; /* registeredID */ }; +#define X509_MAX_SERIAL_NUM_LEN 20 + struct x509_certificate { struct x509_certificate *next; enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; - unsigned long serial_number; + u8 serial_number[X509_MAX_SERIAL_NUM_LEN]; + size_t serial_number_len; struct x509_algorithm_identifier signature; struct x509_name issuer; struct x509_name subject; + u8 *subject_dn; + size_t subject_dn_len; os_time_t not_before; os_time_t not_after; struct x509_algorithm_identifier public_key_alg; @@ -68,6 +73,7 @@ struct x509_certificate { #define X509_EXT_KEY_USAGE (1 << 2) #define X509_EXT_SUBJECT_ALT_NAME (1 << 3) #define X509_EXT_ISSUER_ALT_NAME (1 << 4) +#define X509_EXT_EXT_KEY_USAGE (1 << 5) /* BasicConstraints */ int ca; /* cA */ @@ -85,6 +91,13 @@ struct x509_certificate { #define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) #define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + /* ExtKeyUsage */ + unsigned long ext_key_usage; +#define X509_EXT_KEY_USAGE_ANY (1 << 0) +#define X509_EXT_KEY_USAGE_SERVER_AUTH (1 << 1) +#define X509_EXT_KEY_USAGE_CLIENT_AUTH (1 << 2) +#define X509_EXT_KEY_USAGE_OCSP (1 << 3) + /* * The DER format certificate follows struct x509_certificate. These * pointers point to that buffer. @@ -93,6 +106,11 @@ struct x509_certificate { size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; + + /* Meta data used for certificate validation */ + unsigned int ocsp_good:1; + unsigned int ocsp_revoked:1; + unsigned int issuer_trusted:1; }; enum { @@ -106,10 +124,21 @@ enum { }; void x509_certificate_free(struct x509_certificate *cert); +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next); +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next); +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_free_name(struct x509_name *name); void x509_name_string(struct x509_name *name, char *buf, size_t len); int x509_name_compare(struct x509_name *a, struct x509_name *b); void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len); int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, @@ -120,4 +149,9 @@ x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); +int x509_sha1_oid(struct asn1_oid *oid); +int x509_sha256_oid(struct asn1_oid *oid); +int x509_sha384_oid(struct asn1_oid *oid); +int x509_sha512_oid(struct asn1_oid *oid); + #endif /* X509V3_H */ |