167 lines
4.7 KiB
C
167 lines
4.7 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* Validation functions for ECH public names. */
|
|
|
|
#include "seccomon.h"
|
|
|
|
/* Convert a single character `c` into a number `*d` with the given radix.
|
|
* Fails if the character isn't valid for the radix.
|
|
*/
|
|
static SECStatus
|
|
tls13_IpDigit(PRUint8 c, PRUint8 radix, PRUint8 *d)
|
|
{
|
|
PRUint8 v = 0xff;
|
|
if (c >= '0' && c <= '9') {
|
|
v = c - '0';
|
|
} else if (radix > 10) {
|
|
if (c >= 'a' && c <= 'f') {
|
|
v = c - 'a';
|
|
} else if (c >= 'A' && c <= 'F') {
|
|
v = c - 'A';
|
|
}
|
|
}
|
|
if (v >= radix) {
|
|
return SECFailure;
|
|
}
|
|
*d = v;
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* This function takes the first couple of characters from `str`, starting at offset
|
|
* `*i` and calculates a radix. If it starts with "0x" or "0X", then `*i` is moved up
|
|
* by two and `*radix` is set to 16 (hexadecimal). If it starts with "0", then `*i` is
|
|
* moved up by one and `*radix` is set to 8 (octal). Otherwise, `*i` is left alone and
|
|
* `*radix` is set to 10 (decimal).
|
|
* Fails if there are no characters remaining or the next character is '.', either at
|
|
* the start or after "0x".
|
|
*/
|
|
static SECStatus
|
|
tls13_IpRadix(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint8 *radix)
|
|
{
|
|
if (*i == len || str[*i] == '.') {
|
|
return SECFailure;
|
|
}
|
|
if (str[*i] == '0') {
|
|
(*i)++;
|
|
if (*i < len && (str[*i] == 'x' || str[*i] == 'X')) {
|
|
(*i)++;
|
|
if (*i == len || str[*i] == '.') {
|
|
return SECFailure;
|
|
}
|
|
*radix = 16;
|
|
} else {
|
|
*radix = 8;
|
|
}
|
|
} else {
|
|
*radix = 10;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Take a number from `str` from offset `*i` and put the value in `*v`.
|
|
* This calculates the radix and returns a value between 0 and 2^32-1, using all
|
|
* of the digits up to the end of the string (determined by `len`) or a period ('.').
|
|
* Fails if there is no value, if there a non-digit characters, or if the value is
|
|
* too large.
|
|
*/
|
|
static SECStatus
|
|
tls13_IpValue(const PRUint8 *str, unsigned int len, unsigned int *i, PRUint32 *v)
|
|
{
|
|
PRUint8 radix;
|
|
SECStatus rv = tls13_IpRadix(str, len, i, &radix);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
PRUint64 part = 0;
|
|
while (*i < len) {
|
|
PRUint8 d;
|
|
rv = tls13_IpDigit(str[*i], radix, &d);
|
|
if (rv != SECSuccess) {
|
|
if (str[*i] != '.') {
|
|
return SECFailure;
|
|
}
|
|
break;
|
|
}
|
|
part = part * radix + d;
|
|
if (part > PR_UINT32_MAX) {
|
|
return SECFailure;
|
|
}
|
|
(*i)++;
|
|
}
|
|
*v = part;
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Returns true if `end` is true and `v` is within the `limit`. Used to validate the
|
|
* last part of an IPv4 address, which can hold larger numbers if there are fewer then
|
|
* four parts. */
|
|
static PRBool
|
|
tls13_IpLastPart(PRBool end, PRUint32 v, PRUint32 limit)
|
|
{
|
|
if (!end) {
|
|
return PR_FALSE;
|
|
}
|
|
return v <= limit;
|
|
}
|
|
|
|
/* Returns true if `str` contains an IPv4 address. */
|
|
PRBool
|
|
tls13_IsIp(const PRUint8 *str, unsigned int len)
|
|
{
|
|
PRUint32 part;
|
|
PRUint32 v;
|
|
unsigned int i = 0;
|
|
for (part = 0; part < 4; part++) {
|
|
SECStatus rv = tls13_IpValue(str, len, &i, &v);
|
|
if (rv != SECSuccess) {
|
|
return PR_FALSE;
|
|
}
|
|
if (v > 0xff || i == len) {
|
|
return tls13_IpLastPart(i == len, v, PR_UINT32_MAX >> (part * 8));
|
|
}
|
|
PORT_Assert(str[i] == '.');
|
|
i++;
|
|
}
|
|
|
|
return tls13_IpLastPart(i == len, v, 0xff);
|
|
}
|
|
|
|
static PRBool
|
|
tls13_IsLD(PRUint8 c)
|
|
{
|
|
return (c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == '_'; /* not in spec, but in the world; bug 1136616 */
|
|
}
|
|
|
|
/* Is this a valid dotted LDH string (that is, an A-Label domain name)?
|
|
* This does not tolerate a trailing '.', where the DNS generally does.
|
|
*/
|
|
PRBool
|
|
tls13_IsLDH(const PRUint8 *str, unsigned int len)
|
|
{
|
|
unsigned int i = 0;
|
|
while (i < len && tls13_IsLD(str[i])) {
|
|
unsigned int labelEnd = PR_MIN(len, i + 63);
|
|
i++;
|
|
while (i < labelEnd && (tls13_IsLD(str[i]) || str[i] == '-')) {
|
|
i++;
|
|
}
|
|
if (str[i - 1] == '-') {
|
|
/* labels cannot end in a hyphen */
|
|
return PR_FALSE;
|
|
}
|
|
if (i == len) {
|
|
return PR_TRUE;
|
|
}
|
|
if (str[i] != '.') {
|
|
return PR_FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
return PR_FALSE;
|
|
}
|