lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | |
| 2 | /* Copyright 1998, 2011 by the Massachusetts Institute of Technology. |
| 3 | * |
| 4 | * Permission to use, copy, modify, and distribute this |
| 5 | * software and its documentation for any purpose and without |
| 6 | * fee is hereby granted, provided that the above copyright |
| 7 | * notice appear in all copies and that both that copyright |
| 8 | * notice and this permission notice appear in supporting |
| 9 | * documentation, and that the name of M.I.T. not be used in |
| 10 | * advertising or publicity pertaining to distribution of the |
| 11 | * software without specific, written prior permission. |
| 12 | * M.I.T. makes no representations about the suitability of |
| 13 | * this software for any purpose. It is provided "as is" |
| 14 | * without express or implied warranty. |
| 15 | */ |
| 16 | |
| 17 | #include "ares_setup.h" |
| 18 | |
| 19 | #ifdef HAVE_NETINET_IN_H |
| 20 | # include <netinet/in.h> |
| 21 | #endif |
| 22 | #ifdef HAVE_ARPA_NAMESER_H |
| 23 | # include <arpa/nameser.h> |
| 24 | #else |
| 25 | # include "nameser.h" |
| 26 | #endif |
| 27 | #ifdef HAVE_ARPA_NAMESER_COMPAT_H |
| 28 | # include <arpa/nameser_compat.h> |
| 29 | #endif |
| 30 | |
| 31 | #include "ares.h" |
| 32 | #include "ares_nowarn.h" |
| 33 | #include "ares_private.h" /* for the memdebug */ |
| 34 | |
| 35 | static int name_length(const unsigned char *encoded, const unsigned char *abuf, |
| 36 | int alen); |
| 37 | |
| 38 | /*CVE-2021-3672*/ |
| 39 | /* Reserved characters for names that need to be escaped */ |
| 40 | static int is_reservedch(int ch) |
| 41 | { |
| 42 | switch (ch) { |
| 43 | case '"': |
| 44 | case '.': |
| 45 | case ';': |
| 46 | case '\\': |
| 47 | case '(': |
| 48 | case ')': |
| 49 | case '@': |
| 50 | case '$': |
| 51 | return 1; |
| 52 | default: |
| 53 | break; |
| 54 | } |
| 55 | |
| 56 | return 0; |
| 57 | } |
| 58 | |
| 59 | /* Expand an RFC1035-encoded domain name given by encoded. The |
| 60 | * containing message is given by abuf and alen. The result given by |
| 61 | * *s, which is set to a NUL-terminated allocated buffer. *enclen is |
| 62 | * set to the length of the encoded name (not the length of the |
| 63 | * expanded name; the goal is to tell the caller how many bytes to |
| 64 | * move forward to get past the encoded name). |
| 65 | * |
| 66 | * In the simple case, an encoded name is a series of labels, each |
| 67 | * composed of a one-byte length (limited to values between 0 and 63 |
| 68 | * inclusive) followed by the label contents. The name is terminated |
| 69 | * by a zero-length label. |
| 70 | * |
| 71 | * In the more complicated case, a label may be terminated by an |
| 72 | * indirection pointer, specified by two bytes with the high bits of |
| 73 | * the first byte (corresponding to INDIR_MASK) set to 11. With the |
| 74 | * two high bits of the first byte stripped off, the indirection |
| 75 | * pointer gives an offset from the beginning of the containing |
| 76 | * message with more labels to decode. Indirection can happen an |
| 77 | * arbitrary number of times, so we have to detect loops. |
| 78 | * |
| 79 | * Since the expanded name uses '.' as a label separator, we use |
| 80 | * backslashes to escape periods or backslashes in the expanded name. |
| 81 | */ |
| 82 | |
| 83 | int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, |
| 84 | int alen, char **s, long *enclen) |
| 85 | { |
| 86 | int len, indir = 0; |
| 87 | char *q; |
| 88 | const unsigned char *p; |
| 89 | union { |
| 90 | ssize_t sig; |
| 91 | size_t uns; |
| 92 | } nlen; |
| 93 | |
| 94 | nlen.sig = name_length(encoded, abuf, alen); |
| 95 | if (nlen.sig < 0) |
| 96 | return ARES_EBADNAME; |
| 97 | |
| 98 | *s = ares_malloc(nlen.uns + 1); |
| 99 | if (!*s) |
| 100 | return ARES_ENOMEM; |
| 101 | q = *s; |
| 102 | |
| 103 | if (nlen.uns == 0) { |
| 104 | /* RFC2181 says this should be ".": the root of the DNS tree. |
| 105 | * Since this function strips trailing dots though, it becomes "" |
| 106 | */ |
| 107 | q[0] = '\0'; |
| 108 | |
| 109 | /* indirect root label (like 0xc0 0x0c) is 2 bytes long (stupid, but |
| 110 | valid) */ |
| 111 | if ((*encoded & INDIR_MASK) == INDIR_MASK) |
| 112 | *enclen = 2L; |
| 113 | else |
| 114 | *enclen = 1L; /* the caller should move one byte to get past this */ |
| 115 | |
| 116 | return ARES_SUCCESS; |
| 117 | } |
| 118 | |
| 119 | /* No error-checking necessary; it was all done by name_length(). */ |
| 120 | p = encoded; |
| 121 | while (*p) |
| 122 | { |
| 123 | if ((*p & INDIR_MASK) == INDIR_MASK) |
| 124 | { |
| 125 | if (!indir) |
| 126 | { |
| 127 | *enclen = aresx_uztosl(p + 2U - encoded); |
| 128 | indir = 1; |
| 129 | } |
| 130 | p = abuf + ((*p & ~INDIR_MASK) << 8 | *(p + 1)); |
| 131 | } |
| 132 | else |
| 133 | { |
| 134 | /*CVE-2021-3672*/ |
| 135 | //len = *p; |
| 136 | int name_len = *p; |
| 137 | len = name_len; |
| 138 | p++; |
| 139 | while (len--) |
| 140 | { |
| 141 | /*CVE-2021-3672*/ |
| 142 | //if (*p == '.' || *p == '\\') |
| 143 | // *q++ = '\\'; |
| 144 | //*q++ = *p; |
| 145 | /* Output as \DDD for consistency with RFC1035 5.1, except |
| 146 | * for the special case of a root name response */ |
| 147 | if (!isprint(*p) && !(name_len == 1 && *p == 0)) |
| 148 | { |
| 149 | |
| 150 | *q++ = '\\'; |
| 151 | *q++ = '0' + *p / 100; |
| 152 | *q++ = '0' + (*p % 100) / 10; |
| 153 | *q++ = '0' + (*p % 10); |
| 154 | } |
| 155 | else if (is_reservedch(*p)) |
| 156 | { |
| 157 | *q++ = '\\'; |
| 158 | *q++ = *p; |
| 159 | } |
| 160 | else |
| 161 | { |
| 162 | *q++ = *p; |
| 163 | } |
| 164 | p++; |
| 165 | } |
| 166 | *q++ = '.'; |
| 167 | } |
| 168 | } |
| 169 | if (!indir) |
| 170 | *enclen = aresx_uztosl(p + 1U - encoded); |
| 171 | |
| 172 | /* Nuke the trailing period if we wrote one. */ |
| 173 | if (q > *s) |
| 174 | *(q - 1) = 0; |
| 175 | else |
| 176 | *q = 0; /* zero terminate; LCOV_EXCL_LINE: empty names exit above */ |
| 177 | |
| 178 | return ARES_SUCCESS; |
| 179 | } |
| 180 | |
| 181 | /* Return the length of the expansion of an encoded domain name, or |
| 182 | * -1 if the encoding is invalid. |
| 183 | */ |
| 184 | static int name_length(const unsigned char *encoded, const unsigned char *abuf, |
| 185 | int alen) |
| 186 | { |
| 187 | int n = 0, offset, indir = 0, top; |
| 188 | |
| 189 | /* Allow the caller to pass us abuf + alen and have us check for it. */ |
| 190 | if (encoded >= abuf + alen) |
| 191 | return -1; |
| 192 | |
| 193 | while (*encoded) |
| 194 | { |
| 195 | top = (*encoded & INDIR_MASK); |
| 196 | if (top == INDIR_MASK) |
| 197 | { |
| 198 | /* Check the offset and go there. */ |
| 199 | if (encoded + 1 >= abuf + alen) |
| 200 | return -1; |
| 201 | offset = (*encoded & ~INDIR_MASK) << 8 | *(encoded + 1); |
| 202 | if (offset >= alen) |
| 203 | return -1; |
| 204 | encoded = abuf + offset; |
| 205 | |
| 206 | /* If we've seen more indirects than the message length, |
| 207 | * then there's a loop. |
| 208 | */ |
| 209 | if (++indir > alen) |
| 210 | return -1; |
| 211 | } |
| 212 | else if (top == 0x00) |
| 213 | { |
| 214 | /*CVE-2021-3672*/ |
| 215 | //offset = *encoded; |
| 216 | int name_len = *encoded; |
| 217 | offset = name_len; |
| 218 | if (encoded + offset + 1 >= abuf + alen) |
| 219 | return -1; |
| 220 | encoded++; |
| 221 | while (offset--) |
| 222 | { |
| 223 | /*CVE-2021-3672*/ |
| 224 | //n += (*encoded == '.' || *encoded == '\\') ? 2 : 1; |
| 225 | if (!isprint(*encoded) && !(name_len == 1 && *encoded == 0)) |
| 226 | { |
| 227 | n += 4; |
| 228 | } |
| 229 | else if (is_reservedch(*encoded)) |
| 230 | { |
| 231 | n += 2; |
| 232 | } |
| 233 | else |
| 234 | { |
| 235 | n += 1; |
| 236 | } |
| 237 | encoded++; |
| 238 | } |
| 239 | n++; |
| 240 | } |
| 241 | else |
| 242 | { |
| 243 | /* RFC 1035 4.1.4 says other options (01, 10) for top 2 |
| 244 | * bits are reserved. |
| 245 | */ |
| 246 | return -1; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | /* If there were any labels at all, then the number of dots is one |
| 251 | * less than the number of labels, so subtract one. |
| 252 | */ |
| 253 | return (n) ? n - 1 : n; |
| 254 | } |
| 255 | |
| 256 | /* Like ares_expand_name but returns EBADRESP in case of invalid input. */ |
| 257 | int ares__expand_name_for_response(const unsigned char *encoded, |
| 258 | const unsigned char *abuf, int alen, |
| 259 | char **s, long *enclen) |
| 260 | { |
| 261 | int status = ares_expand_name(encoded, abuf, alen, s, enclen); |
| 262 | if (status == ARES_EBADNAME) |
| 263 | status = ARES_EBADRESP; |
| 264 | return status; |
| 265 | } |