113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci#include "curlcheck.h" 2513498266Sopenharmony_ci 2613498266Sopenharmony_ci#include "doh.h" /* from the lib dir */ 2713498266Sopenharmony_ci 2813498266Sopenharmony_cistatic CURLcode unit_setup(void) 2913498266Sopenharmony_ci{ 3013498266Sopenharmony_ci /* whatever you want done first */ 3113498266Sopenharmony_ci return CURLE_OK; 3213498266Sopenharmony_ci} 3313498266Sopenharmony_ci 3413498266Sopenharmony_cistatic void unit_stop(void) 3513498266Sopenharmony_ci{ 3613498266Sopenharmony_ci /* done before shutting down and exiting */ 3713498266Sopenharmony_ci} 3813498266Sopenharmony_ci 3913498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH 4013498266Sopenharmony_ci 4113498266Sopenharmony_ciUNITTEST_START 4213498266Sopenharmony_ci 4313498266Sopenharmony_ci/* 4413498266Sopenharmony_ci * Prove detection of write overflow using a short buffer and a name 4513498266Sopenharmony_ci * of maximal valid length. 4613498266Sopenharmony_ci * 4713498266Sopenharmony_ci * Prove detection of other invalid input. 4813498266Sopenharmony_ci */ 4913498266Sopenharmony_cido { 5013498266Sopenharmony_ci static const char max[] = 5113498266Sopenharmony_ci /* ..|....1.........2.........3.........4.........5.........6... */ 5213498266Sopenharmony_ci /* 3456789012345678901234567890123456789012345678901234567890123 */ 5313498266Sopenharmony_ci "this.is.a.maximum-length.hostname." /* 34: 34 */ 5413498266Sopenharmony_ci "with-no-label-of-greater-length-than-the-sixty-three-characters." 5513498266Sopenharmony_ci /* 64: 98 */ 5613498266Sopenharmony_ci "specified.in.the.RFCs." /* 22: 120 */ 5713498266Sopenharmony_ci "and.with.a.QNAME.encoding.whose.length.is.exactly." /* 50: 170 */ 5813498266Sopenharmony_ci "the.maximum.length.allowed." /* 27: 197 */ 5913498266Sopenharmony_ci "that.is.two-hundred.and.fifty-six." /* 34: 231 */ 6013498266Sopenharmony_ci "including.the.last.null." /* 24: 255 */ 6113498266Sopenharmony_ci ""; 6213498266Sopenharmony_ci static const char toolong[] = 6313498266Sopenharmony_ci /* ..|....1.........2.........3.........4.........5.........6... */ 6413498266Sopenharmony_ci /* 3456789012345678901234567890123456789012345678901234567890123 */ 6513498266Sopenharmony_ci "here.is.a.hostname.which.is.just.barely.too.long." /* 49: 49 */ 6613498266Sopenharmony_ci "to.be.encoded.as.a.QNAME.of.the.maximum.allowed.length." 6713498266Sopenharmony_ci /* 55: 104 */ 6813498266Sopenharmony_ci "which.is.256.including.a.final.zero-length.label." /* 49: 153 */ 6913498266Sopenharmony_ci "representing.the.root.node.so.that.a.name.with." /* 47: 200 */ 7013498266Sopenharmony_ci "a.trailing.dot.may.have.up.to." /* 30: 230 */ 7113498266Sopenharmony_ci "255.characters.never.more." /* 26: 256 */ 7213498266Sopenharmony_ci ""; 7313498266Sopenharmony_ci static const char emptylabel[] = 7413498266Sopenharmony_ci "this.is.an.otherwise-valid.hostname." 7513498266Sopenharmony_ci ".with.an.empty.label."; 7613498266Sopenharmony_ci static const char outsizelabel[] = 7713498266Sopenharmony_ci "this.is.an.otherwise-valid.hostname." 7813498266Sopenharmony_ci "with-a-label-of-greater-length-than-the-sixty-three-characters-" 7913498266Sopenharmony_ci "specified.in.the.RFCs."; 8013498266Sopenharmony_ci int i; 8113498266Sopenharmony_ci 8213498266Sopenharmony_ci struct test { 8313498266Sopenharmony_ci const char *name; 8413498266Sopenharmony_ci const DOHcode expected_result; 8513498266Sopenharmony_ci }; 8613498266Sopenharmony_ci 8713498266Sopenharmony_ci /* plays the role of struct dnsprobe in urldata.h */ 8813498266Sopenharmony_ci struct demo { 8913498266Sopenharmony_ci unsigned char dohbuffer[255 + 16]; /* deliberately short buffer */ 9013498266Sopenharmony_ci unsigned char canary1; 9113498266Sopenharmony_ci unsigned char canary2; 9213498266Sopenharmony_ci unsigned char canary3; 9313498266Sopenharmony_ci }; 9413498266Sopenharmony_ci 9513498266Sopenharmony_ci const struct test playlist[4] = { 9613498266Sopenharmony_ci { toolong, DOH_DNS_NAME_TOO_LONG }, /* expect early failure */ 9713498266Sopenharmony_ci { emptylabel, DOH_DNS_BAD_LABEL }, /* also */ 9813498266Sopenharmony_ci { outsizelabel, DOH_DNS_BAD_LABEL }, /* also */ 9913498266Sopenharmony_ci { max, DOH_OK } /* expect buffer overwrite */ 10013498266Sopenharmony_ci }; 10113498266Sopenharmony_ci 10213498266Sopenharmony_ci for(i = 0; i < (int)(sizeof(playlist)/sizeof(*playlist)); i++) { 10313498266Sopenharmony_ci const char *name = playlist[i].name; 10413498266Sopenharmony_ci size_t olen = 100000; 10513498266Sopenharmony_ci struct demo victim; 10613498266Sopenharmony_ci DOHcode d; 10713498266Sopenharmony_ci 10813498266Sopenharmony_ci victim.canary1 = 87; /* magic numbers, arbitrarily picked */ 10913498266Sopenharmony_ci victim.canary2 = 35; 11013498266Sopenharmony_ci victim.canary3 = 41; 11113498266Sopenharmony_ci d = doh_encode(name, DNS_TYPE_A, victim.dohbuffer, 11213498266Sopenharmony_ci sizeof(struct demo), /* allow room for overflow */ 11313498266Sopenharmony_ci &olen); 11413498266Sopenharmony_ci 11513498266Sopenharmony_ci fail_unless(d == playlist[i].expected_result, 11613498266Sopenharmony_ci "result returned was not as expected"); 11713498266Sopenharmony_ci if(d == playlist[i].expected_result) { 11813498266Sopenharmony_ci if(name == max) { 11913498266Sopenharmony_ci fail_if(victim.canary1 == 87, 12013498266Sopenharmony_ci "demo one-byte buffer overwrite did not happen"); 12113498266Sopenharmony_ci } 12213498266Sopenharmony_ci else { 12313498266Sopenharmony_ci fail_unless(victim.canary1 == 87, 12413498266Sopenharmony_ci "one-byte buffer overwrite has happened"); 12513498266Sopenharmony_ci } 12613498266Sopenharmony_ci fail_unless(victim.canary2 == 35, 12713498266Sopenharmony_ci "two-byte buffer overwrite has happened"); 12813498266Sopenharmony_ci fail_unless(victim.canary3 == 41, 12913498266Sopenharmony_ci "three-byte buffer overwrite has happened"); 13013498266Sopenharmony_ci } 13113498266Sopenharmony_ci else { 13213498266Sopenharmony_ci if(d == DOH_OK) { 13313498266Sopenharmony_ci fail_unless(olen <= sizeof(victim.dohbuffer), "wrote outside bounds"); 13413498266Sopenharmony_ci fail_unless(olen > strlen(name), "unrealistic low size"); 13513498266Sopenharmony_ci } 13613498266Sopenharmony_ci } 13713498266Sopenharmony_ci } 13813498266Sopenharmony_ci} while(0); 13913498266Sopenharmony_ci 14013498266Sopenharmony_ci/* run normal cases and try to trigger buffer length related errors */ 14113498266Sopenharmony_cido { 14213498266Sopenharmony_ci DNStype dnstype = DNS_TYPE_A; 14313498266Sopenharmony_ci unsigned char buffer[128]; 14413498266Sopenharmony_ci const size_t buflen = sizeof(buffer); 14513498266Sopenharmony_ci const size_t magic1 = 9765; 14613498266Sopenharmony_ci size_t olen1 = magic1; 14713498266Sopenharmony_ci const char *sunshine1 = "a.com"; 14813498266Sopenharmony_ci const char *dotshine1 = "a.com."; 14913498266Sopenharmony_ci const char *sunshine2 = "aa.com"; 15013498266Sopenharmony_ci size_t olen2; 15113498266Sopenharmony_ci DOHcode ret2; 15213498266Sopenharmony_ci size_t olen; 15313498266Sopenharmony_ci 15413498266Sopenharmony_ci DOHcode ret = doh_encode(sunshine1, dnstype, buffer, buflen, &olen1); 15513498266Sopenharmony_ci fail_unless(ret == DOH_OK, "sunshine case 1 should pass fine"); 15613498266Sopenharmony_ci fail_if(olen1 == magic1, "olen has not been assigned properly"); 15713498266Sopenharmony_ci fail_unless(olen1 > strlen(sunshine1), "bad out length"); 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci /* with a trailing dot, the response should have the same length */ 16013498266Sopenharmony_ci olen2 = magic1; 16113498266Sopenharmony_ci ret2 = doh_encode(dotshine1, dnstype, buffer, buflen, &olen2); 16213498266Sopenharmony_ci fail_unless(ret2 == DOH_OK, "dotshine case should pass fine"); 16313498266Sopenharmony_ci fail_if(olen2 == magic1, "olen has not been assigned properly"); 16413498266Sopenharmony_ci fail_unless(olen1 == olen2, "olen should not grow for a trailing dot"); 16513498266Sopenharmony_ci 16613498266Sopenharmony_ci /* add one letter, the response should be one longer */ 16713498266Sopenharmony_ci olen2 = magic1; 16813498266Sopenharmony_ci ret2 = doh_encode(sunshine2, dnstype, buffer, buflen, &olen2); 16913498266Sopenharmony_ci fail_unless(ret2 == DOH_OK, "sunshine case 2 should pass fine"); 17013498266Sopenharmony_ci fail_if(olen2 == magic1, "olen has not been assigned properly"); 17113498266Sopenharmony_ci fail_unless(olen1 + 1 == olen2, "olen should grow with the hostname"); 17213498266Sopenharmony_ci 17313498266Sopenharmony_ci /* pass a short buffer, should fail */ 17413498266Sopenharmony_ci ret = doh_encode(sunshine1, dnstype, buffer, olen1 - 1, &olen); 17513498266Sopenharmony_ci fail_if(ret == DOH_OK, "short buffer should have been noticed"); 17613498266Sopenharmony_ci 17713498266Sopenharmony_ci /* pass a minimum buffer, should succeed */ 17813498266Sopenharmony_ci ret = doh_encode(sunshine1, dnstype, buffer, olen1, &olen); 17913498266Sopenharmony_ci fail_unless(ret == DOH_OK, "minimal length buffer should be long enough"); 18013498266Sopenharmony_ci fail_unless(olen == olen1, "bad buffer length"); 18113498266Sopenharmony_ci} while(0); 18213498266Sopenharmony_ciUNITTEST_STOP 18313498266Sopenharmony_ci 18413498266Sopenharmony_ci#else /* CURL_DISABLE_DOH */ 18513498266Sopenharmony_ci 18613498266Sopenharmony_ciUNITTEST_START 18713498266Sopenharmony_ci/* nothing to do, just succeed */ 18813498266Sopenharmony_ciUNITTEST_STOP 18913498266Sopenharmony_ci 19013498266Sopenharmony_ci#endif 191