xref: /freebsd/contrib/unbound/dnscrypt/dnscrypt.c (revision 24e365220007c415f495cf8dcb228ece6002b8b7)
165b390aaSDag-Erling Smørgrav 
265b390aaSDag-Erling Smørgrav #include "config.h"
365b390aaSDag-Erling Smørgrav #include <stdlib.h>
465b390aaSDag-Erling Smørgrav #include <fcntl.h>
565b390aaSDag-Erling Smørgrav #ifdef HAVE_TIME_H
665b390aaSDag-Erling Smørgrav #include <time.h>
765b390aaSDag-Erling Smørgrav #endif
857bddd21SDag-Erling Smørgrav #include <inttypes.h>
965b390aaSDag-Erling Smørgrav #include <sys/time.h>
1065b390aaSDag-Erling Smørgrav #include <sys/types.h>
1165b390aaSDag-Erling Smørgrav #include "sldns/sbuffer.h"
1265b390aaSDag-Erling Smørgrav #include "util/config_file.h"
1365b390aaSDag-Erling Smørgrav #include "util/net_help.h"
1465b390aaSDag-Erling Smørgrav #include "util/netevent.h"
1565b390aaSDag-Erling Smørgrav #include "util/log.h"
16971980c3SDag-Erling Smørgrav #include "util/storage/slabhash.h"
17971980c3SDag-Erling Smørgrav #include "util/storage/lookup3.h"
1865b390aaSDag-Erling Smørgrav 
1965b390aaSDag-Erling Smørgrav #include "dnscrypt/cert.h"
2065b390aaSDag-Erling Smørgrav #include "dnscrypt/dnscrypt.h"
21c7f4d7adSDag-Erling Smørgrav #include "dnscrypt/dnscrypt_config.h"
2265b390aaSDag-Erling Smørgrav 
2365b390aaSDag-Erling Smørgrav #include <ctype.h>
2465b390aaSDag-Erling Smørgrav 
25971980c3SDag-Erling Smørgrav 
2665b390aaSDag-Erling Smørgrav /**
2765b390aaSDag-Erling Smørgrav  * \file
2865b390aaSDag-Erling Smørgrav  * dnscrypt functions for encrypting DNS packets.
2965b390aaSDag-Erling Smørgrav  */
3065b390aaSDag-Erling Smørgrav 
3165b390aaSDag-Erling Smørgrav #define DNSCRYPT_QUERY_BOX_OFFSET \
32971980c3SDag-Erling Smørgrav     (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + \
33971980c3SDag-Erling Smørgrav     crypto_box_HALF_NONCEBYTES)
3465b390aaSDag-Erling Smørgrav 
3565b390aaSDag-Erling Smørgrav //  8 bytes: magic header (CERT_MAGIC_HEADER)
3665b390aaSDag-Erling Smørgrav // 12 bytes: the client's nonce
3765b390aaSDag-Erling Smørgrav // 12 bytes: server nonce extension
3865b390aaSDag-Erling Smørgrav // 16 bytes: Poly1305 MAC (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES)
3965b390aaSDag-Erling Smørgrav 
4065b390aaSDag-Erling Smørgrav #define DNSCRYPT_REPLY_BOX_OFFSET \
41971980c3SDag-Erling Smørgrav     (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES + \
42971980c3SDag-Erling Smørgrav     crypto_box_HALF_NONCEBYTES)
43971980c3SDag-Erling Smørgrav 
44971980c3SDag-Erling Smørgrav 
45971980c3SDag-Erling Smørgrav /**
46971980c3SDag-Erling Smørgrav  * Shared secret cache key length.
47971980c3SDag-Erling Smørgrav  * secret key.
48971980c3SDag-Erling Smørgrav  * 1 byte: ES_VERSION[1]
49971980c3SDag-Erling Smørgrav  * 32 bytes: client crypto_box_PUBLICKEYBYTES
50971980c3SDag-Erling Smørgrav  * 32 bytes: server crypto_box_SECRETKEYBYTES
51971980c3SDag-Erling Smørgrav  */
52971980c3SDag-Erling Smørgrav #define DNSCRYPT_SHARED_SECRET_KEY_LENGTH \
53971980c3SDag-Erling Smørgrav     (1 + crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES)
54971980c3SDag-Erling Smørgrav 
55971980c3SDag-Erling Smørgrav 
56971980c3SDag-Erling Smørgrav struct shared_secret_cache_key {
57971980c3SDag-Erling Smørgrav     /** the hash table key */
58971980c3SDag-Erling Smørgrav     uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH];
59971980c3SDag-Erling Smørgrav     /** the hash table entry, data is uint8_t pointer of size crypto_box_BEFORENMBYTES which contains the shared secret. */
60971980c3SDag-Erling Smørgrav     struct lruhash_entry entry;
61971980c3SDag-Erling Smørgrav };
62971980c3SDag-Erling Smørgrav 
63971980c3SDag-Erling Smørgrav 
648a384985SDag-Erling Smørgrav struct nonce_cache_key {
658a384985SDag-Erling Smørgrav     /** the nonce used by the client */
668a384985SDag-Erling Smørgrav     uint8_t nonce[crypto_box_HALF_NONCEBYTES];
678a384985SDag-Erling Smørgrav     /** the client_magic used by the client, this is associated to 1 cert only */
688a384985SDag-Erling Smørgrav     uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN];
698a384985SDag-Erling Smørgrav     /** the client public key */
708a384985SDag-Erling Smørgrav     uint8_t client_publickey[crypto_box_PUBLICKEYBYTES];
718a384985SDag-Erling Smørgrav     /** the hash table entry, data is uint8_t */
728a384985SDag-Erling Smørgrav     struct lruhash_entry entry;
738a384985SDag-Erling Smørgrav };
748a384985SDag-Erling Smørgrav 
75971980c3SDag-Erling Smørgrav /**
76971980c3SDag-Erling Smørgrav  * Generate a key suitable to find shared secret in slabhash.
77971980c3SDag-Erling Smørgrav  * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH
78971980c3SDag-Erling Smørgrav  * \param[in] esversion: The es version least significant byte.
79971980c3SDag-Erling Smørgrav  * \param[in] pk: The public key of the client. uint8_t pointer of size
80971980c3SDag-Erling Smørgrav  * crypto_box_PUBLICKEYBYTES.
81971980c3SDag-Erling Smørgrav  * \param[in] sk: The secret key of the server matching the magic query number.
82971980c3SDag-Erling Smørgrav  * uint8_t pointer of size crypto_box_SECRETKEYBYTES.
83971980c3SDag-Erling Smørgrav  * \return the hash of the key.
84971980c3SDag-Erling Smørgrav  */
85971980c3SDag-Erling Smørgrav static uint32_t
dnsc_shared_secrets_cache_key(uint8_t * key,uint8_t esversion,uint8_t * pk,uint8_t * sk)86971980c3SDag-Erling Smørgrav dnsc_shared_secrets_cache_key(uint8_t* key,
87971980c3SDag-Erling Smørgrav                               uint8_t esversion,
88971980c3SDag-Erling Smørgrav                               uint8_t* pk,
89971980c3SDag-Erling Smørgrav                               uint8_t* sk)
90971980c3SDag-Erling Smørgrav {
91971980c3SDag-Erling Smørgrav     key[0] = esversion;
92971980c3SDag-Erling Smørgrav     memcpy(key + 1, pk, crypto_box_PUBLICKEYBYTES);
93971980c3SDag-Erling Smørgrav     memcpy(key + 1 + crypto_box_PUBLICKEYBYTES, sk, crypto_box_SECRETKEYBYTES);
94971980c3SDag-Erling Smørgrav     return hashlittle(key, DNSCRYPT_SHARED_SECRET_KEY_LENGTH, 0);
95971980c3SDag-Erling Smørgrav }
96971980c3SDag-Erling Smørgrav 
97971980c3SDag-Erling Smørgrav /**
98971980c3SDag-Erling Smørgrav  * Inserts a shared secret into the shared_secrets_cache slabhash.
99971980c3SDag-Erling Smørgrav  * The shared secret is copied so the caller can use it freely without caring
100971980c3SDag-Erling Smørgrav  * about the cache entry being evicted or not.
101971980c3SDag-Erling Smørgrav  * \param[in] cache: the slabhash in which to look for the key.
102971980c3SDag-Erling Smørgrav  * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH
103971980c3SDag-Erling Smørgrav  * which contains the key of the shared secret.
104971980c3SDag-Erling Smørgrav  * \param[in] hash: the hash of the key.
105971980c3SDag-Erling Smørgrav  * \param[in] nmkey: a uint8_t pointer of size crypto_box_BEFORENMBYTES which
106971980c3SDag-Erling Smørgrav  * contains the shared secret.
107971980c3SDag-Erling Smørgrav  */
108971980c3SDag-Erling Smørgrav static void
dnsc_shared_secret_cache_insert(struct slabhash * cache,uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH],uint32_t hash,uint8_t nmkey[crypto_box_BEFORENMBYTES])109971980c3SDag-Erling Smørgrav dnsc_shared_secret_cache_insert(struct slabhash *cache,
110971980c3SDag-Erling Smørgrav                                 uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH],
111971980c3SDag-Erling Smørgrav                                 uint32_t hash,
112971980c3SDag-Erling Smørgrav                                 uint8_t nmkey[crypto_box_BEFORENMBYTES])
113971980c3SDag-Erling Smørgrav {
114971980c3SDag-Erling Smørgrav     struct shared_secret_cache_key* k =
115971980c3SDag-Erling Smørgrav         (struct shared_secret_cache_key*)calloc(1, sizeof(*k));
116971980c3SDag-Erling Smørgrav     uint8_t* d = malloc(crypto_box_BEFORENMBYTES);
117971980c3SDag-Erling Smørgrav     if(!k || !d) {
118971980c3SDag-Erling Smørgrav         free(k);
119971980c3SDag-Erling Smørgrav         free(d);
120971980c3SDag-Erling Smørgrav         return;
121971980c3SDag-Erling Smørgrav     }
122971980c3SDag-Erling Smørgrav     memcpy(d, nmkey, crypto_box_BEFORENMBYTES);
123971980c3SDag-Erling Smørgrav     lock_rw_init(&k->entry.lock);
124971980c3SDag-Erling Smørgrav     memcpy(k->key, key, DNSCRYPT_SHARED_SECRET_KEY_LENGTH);
125971980c3SDag-Erling Smørgrav     k->entry.hash = hash;
126971980c3SDag-Erling Smørgrav     k->entry.key = k;
127971980c3SDag-Erling Smørgrav     k->entry.data = d;
128971980c3SDag-Erling Smørgrav     slabhash_insert(cache,
129971980c3SDag-Erling Smørgrav                     hash, &k->entry,
130971980c3SDag-Erling Smørgrav                     d,
131971980c3SDag-Erling Smørgrav                     NULL);
132971980c3SDag-Erling Smørgrav }
133971980c3SDag-Erling Smørgrav 
134971980c3SDag-Erling Smørgrav /**
135971980c3SDag-Erling Smørgrav  * Lookup a record in shared_secrets_cache.
136971980c3SDag-Erling Smørgrav  * \param[in] cache: a pointer to shared_secrets_cache slabhash.
137971980c3SDag-Erling Smørgrav  * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH
138971980c3SDag-Erling Smørgrav  * containing the key to look for.
139971980c3SDag-Erling Smørgrav  * \param[in] hash: a hash of the key.
140971980c3SDag-Erling Smørgrav  * \return a pointer to the locked cache entry or NULL on failure.
141971980c3SDag-Erling Smørgrav  */
142971980c3SDag-Erling Smørgrav static struct lruhash_entry*
dnsc_shared_secrets_lookup(struct slabhash * cache,uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH],uint32_t hash)143971980c3SDag-Erling Smørgrav dnsc_shared_secrets_lookup(struct slabhash* cache,
144971980c3SDag-Erling Smørgrav                            uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH],
145971980c3SDag-Erling Smørgrav                            uint32_t hash)
146971980c3SDag-Erling Smørgrav {
147971980c3SDag-Erling Smørgrav     return slabhash_lookup(cache, hash, key, 0);
148971980c3SDag-Erling Smørgrav }
14965b390aaSDag-Erling Smørgrav 
15065b390aaSDag-Erling Smørgrav /**
1518a384985SDag-Erling Smørgrav  * Generate a key hash suitable to find a nonce in slabhash.
1528a384985SDag-Erling Smørgrav  * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES
1538a384985SDag-Erling Smørgrav  * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN
1548a384985SDag-Erling Smørgrav  * \param[in] pk: The public key of the client. uint8_t pointer of size
1558a384985SDag-Erling Smørgrav  * crypto_box_PUBLICKEYBYTES.
1568a384985SDag-Erling Smørgrav  * \return the hash of the key.
1578a384985SDag-Erling Smørgrav  */
1588a384985SDag-Erling Smørgrav static uint32_t
dnsc_nonce_cache_key_hash(const uint8_t nonce[crypto_box_HALF_NONCEBYTES],const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],const uint8_t pk[crypto_box_PUBLICKEYBYTES])1598a384985SDag-Erling Smørgrav dnsc_nonce_cache_key_hash(const uint8_t nonce[crypto_box_HALF_NONCEBYTES],
1608a384985SDag-Erling Smørgrav                           const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],
1618a384985SDag-Erling Smørgrav                           const uint8_t pk[crypto_box_PUBLICKEYBYTES])
1628a384985SDag-Erling Smørgrav {
1638a384985SDag-Erling Smørgrav     uint32_t h = 0;
1648a384985SDag-Erling Smørgrav     h = hashlittle(nonce, crypto_box_HALF_NONCEBYTES, h);
1658a384985SDag-Erling Smørgrav     h = hashlittle(magic_query, DNSCRYPT_MAGIC_HEADER_LEN, h);
1668a384985SDag-Erling Smørgrav     return hashlittle(pk, crypto_box_PUBLICKEYBYTES, h);
1678a384985SDag-Erling Smørgrav }
1688a384985SDag-Erling Smørgrav 
1698a384985SDag-Erling Smørgrav /**
1708a384985SDag-Erling Smørgrav  * Inserts a nonce, magic_query, pk tuple into the nonces_cache slabhash.
1718a384985SDag-Erling Smørgrav  * \param[in] cache: the slabhash in which to look for the key.
1728a384985SDag-Erling Smørgrav  * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES
1738a384985SDag-Erling Smørgrav  * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN
1748a384985SDag-Erling Smørgrav  * \param[in] pk: The public key of the client. uint8_t pointer of size
1758a384985SDag-Erling Smørgrav  * crypto_box_PUBLICKEYBYTES.
1768a384985SDag-Erling Smørgrav  * \param[in] hash: the hash of the key.
1778a384985SDag-Erling Smørgrav  */
1788a384985SDag-Erling Smørgrav static void
dnsc_nonce_cache_insert(struct slabhash * cache,const uint8_t nonce[crypto_box_HALF_NONCEBYTES],const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],const uint8_t pk[crypto_box_PUBLICKEYBYTES],uint32_t hash)1798a384985SDag-Erling Smørgrav dnsc_nonce_cache_insert(struct slabhash *cache,
1808a384985SDag-Erling Smørgrav                         const uint8_t nonce[crypto_box_HALF_NONCEBYTES],
1818a384985SDag-Erling Smørgrav                         const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],
1828a384985SDag-Erling Smørgrav                         const uint8_t pk[crypto_box_PUBLICKEYBYTES],
1838a384985SDag-Erling Smørgrav                         uint32_t hash)
1848a384985SDag-Erling Smørgrav {
1858a384985SDag-Erling Smørgrav     struct nonce_cache_key* k =
1868a384985SDag-Erling Smørgrav         (struct nonce_cache_key*)calloc(1, sizeof(*k));
1878a384985SDag-Erling Smørgrav     if(!k) {
1888a384985SDag-Erling Smørgrav         free(k);
1898a384985SDag-Erling Smørgrav         return;
1908a384985SDag-Erling Smørgrav     }
1918a384985SDag-Erling Smørgrav     lock_rw_init(&k->entry.lock);
1928a384985SDag-Erling Smørgrav     memcpy(k->nonce, nonce, crypto_box_HALF_NONCEBYTES);
1938a384985SDag-Erling Smørgrav     memcpy(k->magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN);
1948a384985SDag-Erling Smørgrav     memcpy(k->client_publickey, pk, crypto_box_PUBLICKEYBYTES);
1958a384985SDag-Erling Smørgrav     k->entry.hash = hash;
1968a384985SDag-Erling Smørgrav     k->entry.key = k;
1978a384985SDag-Erling Smørgrav     k->entry.data = NULL;
1988a384985SDag-Erling Smørgrav     slabhash_insert(cache,
1998a384985SDag-Erling Smørgrav                     hash, &k->entry,
2008a384985SDag-Erling Smørgrav                     NULL,
2018a384985SDag-Erling Smørgrav                     NULL);
2028a384985SDag-Erling Smørgrav }
2038a384985SDag-Erling Smørgrav 
2048a384985SDag-Erling Smørgrav /**
2058a384985SDag-Erling Smørgrav  * Lookup a record in nonces_cache.
2068a384985SDag-Erling Smørgrav  * \param[in] cache: the slabhash in which to look for the key.
2078a384985SDag-Erling Smørgrav  * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES
2088a384985SDag-Erling Smørgrav  * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN
2098a384985SDag-Erling Smørgrav  * \param[in] pk: The public key of the client. uint8_t pointer of size
2108a384985SDag-Erling Smørgrav  * crypto_box_PUBLICKEYBYTES.
2118a384985SDag-Erling Smørgrav  * \param[in] hash: the hash of the key.
2128a384985SDag-Erling Smørgrav  * \return a pointer to the locked cache entry or NULL on failure.
2138a384985SDag-Erling Smørgrav  */
2148a384985SDag-Erling Smørgrav static struct lruhash_entry*
dnsc_nonces_lookup(struct slabhash * cache,const uint8_t nonce[crypto_box_HALF_NONCEBYTES],const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],const uint8_t pk[crypto_box_PUBLICKEYBYTES],uint32_t hash)2158a384985SDag-Erling Smørgrav dnsc_nonces_lookup(struct slabhash* cache,
2168a384985SDag-Erling Smørgrav                    const uint8_t nonce[crypto_box_HALF_NONCEBYTES],
2178a384985SDag-Erling Smørgrav                    const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN],
2188a384985SDag-Erling Smørgrav                    const uint8_t pk[crypto_box_PUBLICKEYBYTES],
2198a384985SDag-Erling Smørgrav                    uint32_t hash)
2208a384985SDag-Erling Smørgrav {
2218a384985SDag-Erling Smørgrav     struct nonce_cache_key k;
2228a384985SDag-Erling Smørgrav     memset(&k, 0, sizeof(k));
2238a384985SDag-Erling Smørgrav     k.entry.hash = hash;
2248a384985SDag-Erling Smørgrav     memcpy(k.nonce, nonce, crypto_box_HALF_NONCEBYTES);
2258a384985SDag-Erling Smørgrav     memcpy(k.magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN);
2268a384985SDag-Erling Smørgrav     memcpy(k.client_publickey, pk, crypto_box_PUBLICKEYBYTES);
2278a384985SDag-Erling Smørgrav 
2288a384985SDag-Erling Smørgrav     return slabhash_lookup(cache, hash, &k, 0);
2298a384985SDag-Erling Smørgrav }
2308a384985SDag-Erling Smørgrav 
2318a384985SDag-Erling Smørgrav /**
232c7f4d7adSDag-Erling Smørgrav  * Decrypt a query using the dnsccert that was found using dnsc_find_cert.
23365b390aaSDag-Erling Smørgrav  * The client nonce will be extracted from the encrypted query and stored in
23465b390aaSDag-Erling Smørgrav  * client_nonce, a shared secret will be computed and stored in nmkey and the
23565b390aaSDag-Erling Smørgrav  * buffer will be decrypted inplace.
236971980c3SDag-Erling Smørgrav  * \param[in] env the dnscrypt environment.
237c7f4d7adSDag-Erling Smørgrav  * \param[in] cert the cert that matches this encrypted query.
23865b390aaSDag-Erling Smørgrav  * \param[in] client_nonce where the client nonce will be stored.
23965b390aaSDag-Erling Smørgrav  * \param[in] nmkey where the shared secret key will be written.
24065b390aaSDag-Erling Smørgrav  * \param[in] buffer the encrypted buffer.
24165b390aaSDag-Erling Smørgrav  * \return 0 on success.
24265b390aaSDag-Erling Smørgrav  */
24365b390aaSDag-Erling Smørgrav static int
dnscrypt_server_uncurve(struct dnsc_env * env,const dnsccert * cert,uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],uint8_t nmkey[crypto_box_BEFORENMBYTES],struct sldns_buffer * buffer)244971980c3SDag-Erling Smørgrav dnscrypt_server_uncurve(struct dnsc_env* env,
245971980c3SDag-Erling Smørgrav                         const dnsccert *cert,
24665b390aaSDag-Erling Smørgrav                         uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
24765b390aaSDag-Erling Smørgrav                         uint8_t nmkey[crypto_box_BEFORENMBYTES],
24865b390aaSDag-Erling Smørgrav                         struct sldns_buffer* buffer)
24965b390aaSDag-Erling Smørgrav {
25065b390aaSDag-Erling Smørgrav     size_t len = sldns_buffer_limit(buffer);
25165b390aaSDag-Erling Smørgrav     uint8_t *const buf = sldns_buffer_begin(buffer);
25265b390aaSDag-Erling Smørgrav     uint8_t nonce[crypto_box_NONCEBYTES];
25365b390aaSDag-Erling Smørgrav     struct dnscrypt_query_header *query_header;
254971980c3SDag-Erling Smørgrav     // shared secret cache
255971980c3SDag-Erling Smørgrav     uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH];
256971980c3SDag-Erling Smørgrav     struct lruhash_entry* entry;
257971980c3SDag-Erling Smørgrav     uint32_t hash;
25865b390aaSDag-Erling Smørgrav 
2598a384985SDag-Erling Smørgrav     uint32_t nonce_hash;
2608a384985SDag-Erling Smørgrav 
26165b390aaSDag-Erling Smørgrav     if (len <= DNSCRYPT_QUERY_HEADER_SIZE) {
26265b390aaSDag-Erling Smørgrav         return -1;
26365b390aaSDag-Erling Smørgrav     }
26465b390aaSDag-Erling Smørgrav 
26565b390aaSDag-Erling Smørgrav     query_header = (struct dnscrypt_query_header *)buf;
2668a384985SDag-Erling Smørgrav 
2678a384985SDag-Erling Smørgrav     /* Detect replay attacks */
2688a384985SDag-Erling Smørgrav     nonce_hash = dnsc_nonce_cache_key_hash(
2698a384985SDag-Erling Smørgrav         query_header->nonce,
2708a384985SDag-Erling Smørgrav         cert->magic_query,
2718a384985SDag-Erling Smørgrav         query_header->publickey);
2728a384985SDag-Erling Smørgrav 
2738a384985SDag-Erling Smørgrav     lock_basic_lock(&env->nonces_cache_lock);
2748a384985SDag-Erling Smørgrav     entry = dnsc_nonces_lookup(
2758a384985SDag-Erling Smørgrav         env->nonces_cache,
2768a384985SDag-Erling Smørgrav         query_header->nonce,
2778a384985SDag-Erling Smørgrav         cert->magic_query,
2788a384985SDag-Erling Smørgrav         query_header->publickey,
2798a384985SDag-Erling Smørgrav         nonce_hash);
2808a384985SDag-Erling Smørgrav 
2818a384985SDag-Erling Smørgrav     if(entry) {
2828a384985SDag-Erling Smørgrav         lock_rw_unlock(&entry->lock);
2838a384985SDag-Erling Smørgrav         env->num_query_dnscrypt_replay++;
2848a384985SDag-Erling Smørgrav         lock_basic_unlock(&env->nonces_cache_lock);
2858a384985SDag-Erling Smørgrav         return -1;
2868a384985SDag-Erling Smørgrav     }
2878a384985SDag-Erling Smørgrav 
2888a384985SDag-Erling Smørgrav     dnsc_nonce_cache_insert(
2898a384985SDag-Erling Smørgrav         env->nonces_cache,
2908a384985SDag-Erling Smørgrav         query_header->nonce,
2918a384985SDag-Erling Smørgrav         cert->magic_query,
2928a384985SDag-Erling Smørgrav         query_header->publickey,
2938a384985SDag-Erling Smørgrav         nonce_hash);
2948a384985SDag-Erling Smørgrav     lock_basic_unlock(&env->nonces_cache_lock);
2958a384985SDag-Erling Smørgrav 
2968a384985SDag-Erling Smørgrav     /* Find existing shared secret */
297971980c3SDag-Erling Smørgrav     hash = dnsc_shared_secrets_cache_key(key,
298971980c3SDag-Erling Smørgrav                                          cert->es_version[1],
299971980c3SDag-Erling Smørgrav                                          query_header->publickey,
300971980c3SDag-Erling Smørgrav                                          cert->keypair->crypt_secretkey);
301971980c3SDag-Erling Smørgrav     entry = dnsc_shared_secrets_lookup(env->shared_secrets_cache,
302971980c3SDag-Erling Smørgrav                                        key,
303971980c3SDag-Erling Smørgrav                                        hash);
304971980c3SDag-Erling Smørgrav 
305971980c3SDag-Erling Smørgrav     if(!entry) {
306971980c3SDag-Erling Smørgrav         lock_basic_lock(&env->shared_secrets_cache_lock);
307971980c3SDag-Erling Smørgrav         env->num_query_dnscrypt_secret_missed_cache++;
308971980c3SDag-Erling Smørgrav         lock_basic_unlock(&env->shared_secrets_cache_lock);
309c7f4d7adSDag-Erling Smørgrav         if(cert->es_version[1] == 2) {
310c7f4d7adSDag-Erling Smørgrav #ifdef USE_DNSCRYPT_XCHACHA20
311c7f4d7adSDag-Erling Smørgrav             if (crypto_box_curve25519xchacha20poly1305_beforenm(
312971980c3SDag-Erling Smørgrav                         nmkey, query_header->publickey,
313971980c3SDag-Erling Smørgrav                         cert->keypair->crypt_secretkey) != 0) {
31465b390aaSDag-Erling Smørgrav                 return -1;
31565b390aaSDag-Erling Smørgrav             }
316c7f4d7adSDag-Erling Smørgrav #else
317c7f4d7adSDag-Erling Smørgrav             return -1;
318c7f4d7adSDag-Erling Smørgrav #endif
319c7f4d7adSDag-Erling Smørgrav 	} else {
320971980c3SDag-Erling Smørgrav 	    if (crypto_box_beforenm(nmkey,
321971980c3SDag-Erling Smørgrav 				    query_header->publickey,
322971980c3SDag-Erling Smørgrav 				    cert->keypair->crypt_secretkey) != 0) {
323c7f4d7adSDag-Erling Smørgrav 		return -1;
324c7f4d7adSDag-Erling Smørgrav 	    }
325c7f4d7adSDag-Erling Smørgrav 	}
326971980c3SDag-Erling Smørgrav         // Cache the shared secret we just computed.
327971980c3SDag-Erling Smørgrav         dnsc_shared_secret_cache_insert(env->shared_secrets_cache,
328971980c3SDag-Erling Smørgrav                                     key,
329971980c3SDag-Erling Smørgrav                                     hash,
330971980c3SDag-Erling Smørgrav                                     nmkey);
331971980c3SDag-Erling Smørgrav     } else {
332971980c3SDag-Erling Smørgrav         /* copy shared secret and unlock entry */
333971980c3SDag-Erling Smørgrav         memcpy(nmkey, entry->data, crypto_box_BEFORENMBYTES);
334971980c3SDag-Erling Smørgrav         lock_rw_unlock(&entry->lock);
335971980c3SDag-Erling Smørgrav     }
33665b390aaSDag-Erling Smørgrav 
33765b390aaSDag-Erling Smørgrav     memcpy(nonce, query_header->nonce, crypto_box_HALF_NONCEBYTES);
33865b390aaSDag-Erling Smørgrav     memset(nonce + crypto_box_HALF_NONCEBYTES, 0, crypto_box_HALF_NONCEBYTES);
33965b390aaSDag-Erling Smørgrav 
340c7f4d7adSDag-Erling Smørgrav     if(cert->es_version[1] == 2) {
341c7f4d7adSDag-Erling Smørgrav #ifdef USE_DNSCRYPT_XCHACHA20
342c7f4d7adSDag-Erling Smørgrav         if (crypto_box_curve25519xchacha20poly1305_open_easy_afternm
343c7f4d7adSDag-Erling Smørgrav                 (buf,
344c7f4d7adSDag-Erling Smørgrav                 buf + DNSCRYPT_QUERY_BOX_OFFSET,
345c7f4d7adSDag-Erling Smørgrav                 len - DNSCRYPT_QUERY_BOX_OFFSET, nonce,
34665b390aaSDag-Erling Smørgrav                 nmkey) != 0) {
34765b390aaSDag-Erling Smørgrav             return -1;
34865b390aaSDag-Erling Smørgrav         }
349c7f4d7adSDag-Erling Smørgrav #else
350c7f4d7adSDag-Erling Smørgrav         return -1;
351c7f4d7adSDag-Erling Smørgrav #endif
352c7f4d7adSDag-Erling Smørgrav     } else {
353c7f4d7adSDag-Erling Smørgrav         if (crypto_box_open_easy_afternm
354c7f4d7adSDag-Erling Smørgrav             (buf,
355c7f4d7adSDag-Erling Smørgrav              buf + DNSCRYPT_QUERY_BOX_OFFSET,
356c7f4d7adSDag-Erling Smørgrav              len - DNSCRYPT_QUERY_BOX_OFFSET, nonce,
357c7f4d7adSDag-Erling Smørgrav              nmkey) != 0) {
358c7f4d7adSDag-Erling Smørgrav             return -1;
359c7f4d7adSDag-Erling Smørgrav         }
360c7f4d7adSDag-Erling Smørgrav     }
361c7f4d7adSDag-Erling Smørgrav 
362c7f4d7adSDag-Erling Smørgrav     len -= DNSCRYPT_QUERY_HEADER_SIZE;
36365b390aaSDag-Erling Smørgrav 
36465b390aaSDag-Erling Smørgrav     while (*sldns_buffer_at(buffer, --len) == 0)
36565b390aaSDag-Erling Smørgrav         ;
36665b390aaSDag-Erling Smørgrav 
36765b390aaSDag-Erling Smørgrav     if (*sldns_buffer_at(buffer, len) != 0x80) {
36865b390aaSDag-Erling Smørgrav         return -1;
36965b390aaSDag-Erling Smørgrav     }
37065b390aaSDag-Erling Smørgrav 
37165b390aaSDag-Erling Smørgrav     memcpy(client_nonce, nonce, crypto_box_HALF_NONCEBYTES);
37265b390aaSDag-Erling Smørgrav 
37365b390aaSDag-Erling Smørgrav     sldns_buffer_set_position(buffer, 0);
374c7f4d7adSDag-Erling Smørgrav     sldns_buffer_set_limit(buffer, len);
37565b390aaSDag-Erling Smørgrav 
37665b390aaSDag-Erling Smørgrav     return 0;
37765b390aaSDag-Erling Smørgrav }
37865b390aaSDag-Erling Smørgrav 
37965b390aaSDag-Erling Smørgrav 
38065b390aaSDag-Erling Smørgrav /**
38165b390aaSDag-Erling Smørgrav  * Add random padding to a buffer, according to a client nonce.
38265b390aaSDag-Erling Smørgrav  * The length has to depend on the query in order to avoid reply attacks.
38365b390aaSDag-Erling Smørgrav  *
38465b390aaSDag-Erling Smørgrav  * @param buf a buffer
38565b390aaSDag-Erling Smørgrav  * @param len the initial size of the buffer
38665b390aaSDag-Erling Smørgrav  * @param max_len the maximum size
38765b390aaSDag-Erling Smørgrav  * @param nonce a nonce, made of the client nonce repeated twice
38865b390aaSDag-Erling Smørgrav  * @param secretkey
38965b390aaSDag-Erling Smørgrav  * @return the new size, after padding
39065b390aaSDag-Erling Smørgrav  */
39165b390aaSDag-Erling Smørgrav size_t
dnscrypt_pad(uint8_t * buf,const size_t len,const size_t max_len,const uint8_t * nonce,const uint8_t * secretkey)39265b390aaSDag-Erling Smørgrav dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len,
39365b390aaSDag-Erling Smørgrav              const uint8_t *nonce, const uint8_t *secretkey)
39465b390aaSDag-Erling Smørgrav {
39565b390aaSDag-Erling Smørgrav     uint8_t *buf_padding_area = buf + len;
39665b390aaSDag-Erling Smørgrav     size_t padded_len;
39765b390aaSDag-Erling Smørgrav     uint32_t rnd;
39865b390aaSDag-Erling Smørgrav 
39965b390aaSDag-Erling Smørgrav     // no padding
40065b390aaSDag-Erling Smørgrav     if (max_len < len + DNSCRYPT_MIN_PAD_LEN)
40165b390aaSDag-Erling Smørgrav         return len;
40265b390aaSDag-Erling Smørgrav 
40365b390aaSDag-Erling Smørgrav     assert(nonce[crypto_box_HALF_NONCEBYTES] == nonce[0]);
40465b390aaSDag-Erling Smørgrav 
40565b390aaSDag-Erling Smørgrav     crypto_stream((unsigned char *)&rnd, (unsigned long long)sizeof(rnd), nonce,
40665b390aaSDag-Erling Smørgrav                   secretkey);
40765b390aaSDag-Erling Smørgrav     padded_len =
40865b390aaSDag-Erling Smørgrav         len + DNSCRYPT_MIN_PAD_LEN + rnd % (max_len - len -
40965b390aaSDag-Erling Smørgrav                                             DNSCRYPT_MIN_PAD_LEN + 1);
41065b390aaSDag-Erling Smørgrav     padded_len += DNSCRYPT_BLOCK_SIZE - padded_len % DNSCRYPT_BLOCK_SIZE;
41165b390aaSDag-Erling Smørgrav     if (padded_len > max_len)
41265b390aaSDag-Erling Smørgrav         padded_len = max_len;
41365b390aaSDag-Erling Smørgrav 
41465b390aaSDag-Erling Smørgrav     memset(buf_padding_area, 0, padded_len - len);
41565b390aaSDag-Erling Smørgrav     *buf_padding_area = 0x80;
41665b390aaSDag-Erling Smørgrav 
41765b390aaSDag-Erling Smørgrav     return padded_len;
41865b390aaSDag-Erling Smørgrav }
41965b390aaSDag-Erling Smørgrav 
42065b390aaSDag-Erling Smørgrav uint64_t
dnscrypt_hrtime(void)42165b390aaSDag-Erling Smørgrav dnscrypt_hrtime(void)
42265b390aaSDag-Erling Smørgrav {
42365b390aaSDag-Erling Smørgrav     struct timeval tv;
42465b390aaSDag-Erling Smørgrav     uint64_t ts = (uint64_t)0U;
42565b390aaSDag-Erling Smørgrav     int ret;
42665b390aaSDag-Erling Smørgrav 
42765b390aaSDag-Erling Smørgrav     ret = gettimeofday(&tv, NULL);
42865b390aaSDag-Erling Smørgrav     if (ret == 0) {
42965b390aaSDag-Erling Smørgrav         ts = (uint64_t)tv.tv_sec * 1000000U + (uint64_t)tv.tv_usec;
43065b390aaSDag-Erling Smørgrav     } else {
43165b390aaSDag-Erling Smørgrav         log_err("gettimeofday: %s", strerror(errno));
43265b390aaSDag-Erling Smørgrav     }
43365b390aaSDag-Erling Smørgrav     return ts;
43465b390aaSDag-Erling Smørgrav }
43565b390aaSDag-Erling Smørgrav 
43665b390aaSDag-Erling Smørgrav /**
43765b390aaSDag-Erling Smørgrav  * Add the server nonce part to once.
438*24e36522SCy Schubert  * The nonce is made half of client nonce and the second half of the server
43965b390aaSDag-Erling Smørgrav  * nonce, both of them of size crypto_box_HALF_NONCEBYTES.
44065b390aaSDag-Erling Smørgrav  * \param[in] nonce: a uint8_t* of size crypto_box_NONCEBYTES
44165b390aaSDag-Erling Smørgrav  */
44265b390aaSDag-Erling Smørgrav static void
add_server_nonce(uint8_t * nonce)44365b390aaSDag-Erling Smørgrav add_server_nonce(uint8_t *nonce)
44465b390aaSDag-Erling Smørgrav {
4450eefd307SCy Schubert     randombytes_buf(nonce + crypto_box_HALF_NONCEBYTES, 8/*tsn*/+4/*suffix*/);
44665b390aaSDag-Erling Smørgrav }
44765b390aaSDag-Erling Smørgrav 
44865b390aaSDag-Erling Smørgrav /**
449c7f4d7adSDag-Erling Smørgrav  * Encrypt a reply using the dnsccert that was used with the query.
45065b390aaSDag-Erling Smørgrav  * The client nonce will be extracted from the encrypted query and stored in
45165b390aaSDag-Erling Smørgrav  * The buffer will be encrypted inplace.
452c7f4d7adSDag-Erling Smørgrav  * \param[in] cert the dnsccert that matches this encrypted query.
45365b390aaSDag-Erling Smørgrav  * \param[in] client_nonce client nonce used during the query
45465b390aaSDag-Erling Smørgrav  * \param[in] nmkey shared secret key used during the query.
45565b390aaSDag-Erling Smørgrav  * \param[in] buffer the buffer where to encrypt the reply.
45665b390aaSDag-Erling Smørgrav  * \param[in] udp if whether or not it is a UDP query.
45765b390aaSDag-Erling Smørgrav  * \param[in] max_udp_size configured max udp size.
45865b390aaSDag-Erling Smørgrav  * \return 0 on success.
45965b390aaSDag-Erling Smørgrav  */
46065b390aaSDag-Erling Smørgrav static int
dnscrypt_server_curve(const dnsccert * cert,uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],uint8_t nmkey[crypto_box_BEFORENMBYTES],struct sldns_buffer * buffer,uint8_t udp,size_t max_udp_size)461c7f4d7adSDag-Erling Smørgrav dnscrypt_server_curve(const dnsccert *cert,
46265b390aaSDag-Erling Smørgrav                       uint8_t client_nonce[crypto_box_HALF_NONCEBYTES],
46365b390aaSDag-Erling Smørgrav                       uint8_t nmkey[crypto_box_BEFORENMBYTES],
46465b390aaSDag-Erling Smørgrav                       struct sldns_buffer* buffer,
46565b390aaSDag-Erling Smørgrav                       uint8_t udp,
46665b390aaSDag-Erling Smørgrav                       size_t max_udp_size)
46765b390aaSDag-Erling Smørgrav {
46865b390aaSDag-Erling Smørgrav     size_t dns_reply_len = sldns_buffer_limit(buffer);
469971980c3SDag-Erling Smørgrav     size_t max_len = dns_reply_len + DNSCRYPT_MAX_PADDING \
470971980c3SDag-Erling Smørgrav         + DNSCRYPT_REPLY_HEADER_SIZE;
47165b390aaSDag-Erling Smørgrav     size_t max_reply_size = max_udp_size - 20U - 8U;
47265b390aaSDag-Erling Smørgrav     uint8_t nonce[crypto_box_NONCEBYTES];
47365b390aaSDag-Erling Smørgrav     uint8_t *boxed;
47465b390aaSDag-Erling Smørgrav     uint8_t *const buf = sldns_buffer_begin(buffer);
47565b390aaSDag-Erling Smørgrav     size_t len = sldns_buffer_limit(buffer);
47665b390aaSDag-Erling Smørgrav 
47765b390aaSDag-Erling Smørgrav     if(udp){
47865b390aaSDag-Erling Smørgrav         if (max_len > max_reply_size)
47965b390aaSDag-Erling Smørgrav             max_len = max_reply_size;
48065b390aaSDag-Erling Smørgrav     }
48165b390aaSDag-Erling Smørgrav 
48265b390aaSDag-Erling Smørgrav 
48365b390aaSDag-Erling Smørgrav     memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES);
48465b390aaSDag-Erling Smørgrav     memcpy(nonce + crypto_box_HALF_NONCEBYTES, client_nonce,
48565b390aaSDag-Erling Smørgrav            crypto_box_HALF_NONCEBYTES);
48665b390aaSDag-Erling Smørgrav 
48765b390aaSDag-Erling Smørgrav     boxed = buf + DNSCRYPT_REPLY_BOX_OFFSET;
48865b390aaSDag-Erling Smørgrav     memmove(boxed + crypto_box_MACBYTES, buf, len);
48965b390aaSDag-Erling Smørgrav     len = dnscrypt_pad(boxed + crypto_box_MACBYTES, len,
49065b390aaSDag-Erling Smørgrav                        max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce,
491c7f4d7adSDag-Erling Smørgrav                        cert->keypair->crypt_secretkey);
49265b390aaSDag-Erling Smørgrav     sldns_buffer_set_at(buffer,
49365b390aaSDag-Erling Smørgrav                         DNSCRYPT_REPLY_BOX_OFFSET - crypto_box_BOXZEROBYTES,
49465b390aaSDag-Erling Smørgrav                         0, crypto_box_ZEROBYTES);
49565b390aaSDag-Erling Smørgrav 
49665b390aaSDag-Erling Smørgrav     // add server nonce extension
49765b390aaSDag-Erling Smørgrav     add_server_nonce(nonce);
49865b390aaSDag-Erling Smørgrav 
499c7f4d7adSDag-Erling Smørgrav     if(cert->es_version[1] == 2) {
500c7f4d7adSDag-Erling Smørgrav #ifdef USE_DNSCRYPT_XCHACHA20
501c7f4d7adSDag-Erling Smørgrav         if (crypto_box_curve25519xchacha20poly1305_easy_afternm
502c7f4d7adSDag-Erling Smørgrav             (boxed, boxed + crypto_box_MACBYTES, len, nonce, nmkey) != 0) {
50365b390aaSDag-Erling Smørgrav             return -1;
50465b390aaSDag-Erling Smørgrav         }
505c7f4d7adSDag-Erling Smørgrav #else
506c7f4d7adSDag-Erling Smørgrav         return -1;
507c7f4d7adSDag-Erling Smørgrav #endif
508c7f4d7adSDag-Erling Smørgrav     } else {
509c7f4d7adSDag-Erling Smørgrav         if (crypto_box_easy_afternm
510c7f4d7adSDag-Erling Smørgrav             (boxed, boxed + crypto_box_MACBYTES, len, nonce, nmkey) != 0) {
511c7f4d7adSDag-Erling Smørgrav             return -1;
512c7f4d7adSDag-Erling Smørgrav         }
513c7f4d7adSDag-Erling Smørgrav     }
51465b390aaSDag-Erling Smørgrav 
515971980c3SDag-Erling Smørgrav     sldns_buffer_write_at(buffer,
516971980c3SDag-Erling Smørgrav                           0,
517971980c3SDag-Erling Smørgrav                           DNSCRYPT_MAGIC_RESPONSE,
518971980c3SDag-Erling Smørgrav                           DNSCRYPT_MAGIC_HEADER_LEN);
519971980c3SDag-Erling Smørgrav     sldns_buffer_write_at(buffer,
520971980c3SDag-Erling Smørgrav                           DNSCRYPT_MAGIC_HEADER_LEN,
521971980c3SDag-Erling Smørgrav                           nonce,
522971980c3SDag-Erling Smørgrav                           crypto_box_NONCEBYTES);
52365b390aaSDag-Erling Smørgrav     sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE);
52465b390aaSDag-Erling Smørgrav     return 0;
52565b390aaSDag-Erling Smørgrav }
52665b390aaSDag-Erling Smørgrav 
52765b390aaSDag-Erling Smørgrav /**
52865b390aaSDag-Erling Smørgrav  * Read the content of fname into buf.
52965b390aaSDag-Erling Smørgrav  * \param[in] fname name of the file to read.
53065b390aaSDag-Erling Smørgrav  * \param[in] buf the buffer in which to read the content of the file.
53165b390aaSDag-Erling Smørgrav  * \param[in] count number of bytes to read.
53265b390aaSDag-Erling Smørgrav  * \return 0 on success.
53365b390aaSDag-Erling Smørgrav  */
53465b390aaSDag-Erling Smørgrav static int
dnsc_read_from_file(char * fname,char * buf,size_t count)53565b390aaSDag-Erling Smørgrav dnsc_read_from_file(char *fname, char *buf, size_t count)
53665b390aaSDag-Erling Smørgrav {
53765b390aaSDag-Erling Smørgrav     int fd;
53865b390aaSDag-Erling Smørgrav     fd = open(fname, O_RDONLY);
53965b390aaSDag-Erling Smørgrav     if (fd == -1) {
54065b390aaSDag-Erling Smørgrav         return -1;
54165b390aaSDag-Erling Smørgrav     }
54265b390aaSDag-Erling Smørgrav     if (read(fd, buf, count) != (ssize_t)count) {
54365b390aaSDag-Erling Smørgrav         close(fd);
54465b390aaSDag-Erling Smørgrav         return -2;
54565b390aaSDag-Erling Smørgrav     }
54665b390aaSDag-Erling Smørgrav     close(fd);
54765b390aaSDag-Erling Smørgrav     return 0;
54865b390aaSDag-Erling Smørgrav }
54965b390aaSDag-Erling Smørgrav 
55065b390aaSDag-Erling Smørgrav /**
551c7f4d7adSDag-Erling Smørgrav  * Given an absolute path on the original root, returns the absolute path
552c7f4d7adSDag-Erling Smørgrav  * within the chroot. If chroot is disabled, the path is not modified.
553c7f4d7adSDag-Erling Smørgrav  * No char * is malloced so there is no need to free this.
554c7f4d7adSDag-Erling Smørgrav  * \param[in] cfg the configuration.
555c7f4d7adSDag-Erling Smørgrav  * \param[in] path the path from the original root.
556c7f4d7adSDag-Erling Smørgrav  * \return the path from inside the chroot.
557c7f4d7adSDag-Erling Smørgrav  */
558c7f4d7adSDag-Erling Smørgrav static char *
dnsc_chroot_path(struct config_file * cfg,char * path)559c7f4d7adSDag-Erling Smørgrav dnsc_chroot_path(struct config_file *cfg, char *path)
560c7f4d7adSDag-Erling Smørgrav {
561c7f4d7adSDag-Erling Smørgrav     char *nm;
562c7f4d7adSDag-Erling Smørgrav     nm = path;
563c7f4d7adSDag-Erling Smørgrav     if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
564c7f4d7adSDag-Erling Smørgrav         cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
565c7f4d7adSDag-Erling Smørgrav         nm += strlen(cfg->chrootdir);
566c7f4d7adSDag-Erling Smørgrav     return nm;
567c7f4d7adSDag-Erling Smørgrav }
568c7f4d7adSDag-Erling Smørgrav 
569c7f4d7adSDag-Erling Smørgrav /**
57065b390aaSDag-Erling Smørgrav  * Parse certificates files provided by the configuration and load them into
57165b390aaSDag-Erling Smørgrav  * dnsc_env.
57265b390aaSDag-Erling Smørgrav  * \param[in] env the dnsc_env structure to load the certs into.
57365b390aaSDag-Erling Smørgrav  * \param[in] cfg the configuration.
57465b390aaSDag-Erling Smørgrav  * \return the number of certificates loaded.
57565b390aaSDag-Erling Smørgrav  */
57665b390aaSDag-Erling Smørgrav static int
dnsc_parse_certs(struct dnsc_env * env,struct config_file * cfg)57765b390aaSDag-Erling Smørgrav dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg)
57865b390aaSDag-Erling Smørgrav {
57957bddd21SDag-Erling Smørgrav 	struct config_strlist *head, *head2;
58065b390aaSDag-Erling Smørgrav 	size_t signed_cert_id;
58157bddd21SDag-Erling Smørgrav 	size_t rotated_cert_id;
582c7f4d7adSDag-Erling Smørgrav 	char *nm;
58365b390aaSDag-Erling Smørgrav 
58465b390aaSDag-Erling Smørgrav 	env->signed_certs_count = 0U;
58557bddd21SDag-Erling Smørgrav 	env->rotated_certs_count = 0U;
58665b390aaSDag-Erling Smørgrav 	for (head = cfg->dnscrypt_provider_cert; head; head = head->next) {
58765b390aaSDag-Erling Smørgrav 		env->signed_certs_count++;
58865b390aaSDag-Erling Smørgrav 	}
58957bddd21SDag-Erling Smørgrav 	for (head = cfg->dnscrypt_provider_cert_rotated; head; head = head->next) {
59057bddd21SDag-Erling Smørgrav 		env->rotated_certs_count++;
59157bddd21SDag-Erling Smørgrav 	}
59265b390aaSDag-Erling Smørgrav 	env->signed_certs = sodium_allocarray(env->signed_certs_count,
59365b390aaSDag-Erling Smørgrav 										  sizeof *env->signed_certs);
59465b390aaSDag-Erling Smørgrav 
59557bddd21SDag-Erling Smørgrav 	env->rotated_certs = sodium_allocarray(env->rotated_certs_count,
59657bddd21SDag-Erling Smørgrav 										  sizeof env->signed_certs);
59765b390aaSDag-Erling Smørgrav 	signed_cert_id = 0U;
59857bddd21SDag-Erling Smørgrav 	rotated_cert_id = 0U;
59965b390aaSDag-Erling Smørgrav 	for(head = cfg->dnscrypt_provider_cert; head; head = head->next, signed_cert_id++) {
600c7f4d7adSDag-Erling Smørgrav 		nm = dnsc_chroot_path(cfg, head->str);
60165b390aaSDag-Erling Smørgrav 		if(dnsc_read_from_file(
602c7f4d7adSDag-Erling Smørgrav 				nm,
60365b390aaSDag-Erling Smørgrav 				(char *)(env->signed_certs + signed_cert_id),
60465b390aaSDag-Erling Smørgrav 				sizeof(struct SignedCert)) != 0) {
60565b390aaSDag-Erling Smørgrav 			fatal_exit("dnsc_parse_certs: failed to load %s: %s", head->str, strerror(errno));
60665b390aaSDag-Erling Smørgrav 		}
60757bddd21SDag-Erling Smørgrav 		for(head2 = cfg->dnscrypt_provider_cert_rotated; head2; head2 = head2->next) {
60857bddd21SDag-Erling Smørgrav 			if(strcmp(head->str, head2->str) == 0) {
60957bddd21SDag-Erling Smørgrav 				*(env->rotated_certs + rotated_cert_id) = env->signed_certs + signed_cert_id;
61057bddd21SDag-Erling Smørgrav 				rotated_cert_id++;
61157bddd21SDag-Erling Smørgrav 				verbose(VERB_OPS, "Cert %s is rotated and will not be distributed via DNS", head->str);
61257bddd21SDag-Erling Smørgrav 				break;
61357bddd21SDag-Erling Smørgrav 			}
61457bddd21SDag-Erling Smørgrav 		}
61565b390aaSDag-Erling Smørgrav 		verbose(VERB_OPS, "Loaded cert %s", head->str);
61665b390aaSDag-Erling Smørgrav 	}
61765b390aaSDag-Erling Smørgrav 	return signed_cert_id;
61865b390aaSDag-Erling Smørgrav }
61965b390aaSDag-Erling Smørgrav 
62065b390aaSDag-Erling Smørgrav /**
62165b390aaSDag-Erling Smørgrav  * Helper function to convert a binary key into a printable fingerprint.
62265b390aaSDag-Erling Smørgrav  * \param[in] fingerprint the buffer in which to write the printable key.
62365b390aaSDag-Erling Smørgrav  * \param[in] key the key to convert.
62465b390aaSDag-Erling Smørgrav  */
62565b390aaSDag-Erling Smørgrav void
dnsc_key_to_fingerprint(char fingerprint[80U],const uint8_t * const key)62665b390aaSDag-Erling Smørgrav dnsc_key_to_fingerprint(char fingerprint[80U], const uint8_t * const key)
62765b390aaSDag-Erling Smørgrav {
62865b390aaSDag-Erling Smørgrav     const size_t fingerprint_size = 80U;
62965b390aaSDag-Erling Smørgrav     size_t       fingerprint_pos = (size_t) 0U;
63065b390aaSDag-Erling Smørgrav     size_t       key_pos = (size_t) 0U;
63165b390aaSDag-Erling Smørgrav 
63265b390aaSDag-Erling Smørgrav     for (;;) {
63365b390aaSDag-Erling Smørgrav         assert(fingerprint_size > fingerprint_pos);
63465b390aaSDag-Erling Smørgrav         snprintf(&fingerprint[fingerprint_pos],
63565b390aaSDag-Erling Smørgrav                         fingerprint_size - fingerprint_pos, "%02X%02X",
63665b390aaSDag-Erling Smørgrav                         key[key_pos], key[key_pos + 1U]);
63765b390aaSDag-Erling Smørgrav         key_pos += 2U;
63865b390aaSDag-Erling Smørgrav         if (key_pos >= crypto_box_PUBLICKEYBYTES) {
63965b390aaSDag-Erling Smørgrav             break;
64065b390aaSDag-Erling Smørgrav         }
64165b390aaSDag-Erling Smørgrav         fingerprint[fingerprint_pos + 4U] = ':';
64265b390aaSDag-Erling Smørgrav         fingerprint_pos += 5U;
64365b390aaSDag-Erling Smørgrav     }
64465b390aaSDag-Erling Smørgrav }
64565b390aaSDag-Erling Smørgrav 
64665b390aaSDag-Erling Smørgrav /**
647c7f4d7adSDag-Erling Smørgrav  * Find the cert matching a DNSCrypt query.
648971980c3SDag-Erling Smørgrav  * \param[in] dnscenv The DNSCrypt environment, which contains the list of certs
64965b390aaSDag-Erling Smørgrav  * supported by the server.
65065b390aaSDag-Erling Smørgrav  * \param[in] buffer The encrypted DNS query.
651c7f4d7adSDag-Erling Smørgrav  * \return a dnsccert * if we found a cert matching the magic_number of the
652c7f4d7adSDag-Erling Smørgrav  * query, NULL otherwise.
65365b390aaSDag-Erling Smørgrav  */
654c7f4d7adSDag-Erling Smørgrav static const dnsccert *
dnsc_find_cert(struct dnsc_env * dnscenv,struct sldns_buffer * buffer)655c7f4d7adSDag-Erling Smørgrav dnsc_find_cert(struct dnsc_env* dnscenv, struct sldns_buffer* buffer)
65665b390aaSDag-Erling Smørgrav {
657c7f4d7adSDag-Erling Smørgrav 	const dnsccert *certs = dnscenv->certs;
65865b390aaSDag-Erling Smørgrav 	struct dnscrypt_query_header *dnscrypt_header;
65965b390aaSDag-Erling Smørgrav 	size_t i;
66065b390aaSDag-Erling Smørgrav 
66165b390aaSDag-Erling Smørgrav 	if (sldns_buffer_limit(buffer) < DNSCRYPT_QUERY_HEADER_SIZE) {
66265b390aaSDag-Erling Smørgrav 		return NULL;
66365b390aaSDag-Erling Smørgrav 	}
66465b390aaSDag-Erling Smørgrav 	dnscrypt_header = (struct dnscrypt_query_header *)sldns_buffer_begin(buffer);
665c7f4d7adSDag-Erling Smørgrav 	for (i = 0U; i < dnscenv->signed_certs_count; i++) {
666c7f4d7adSDag-Erling Smørgrav 		if (memcmp(certs[i].magic_query, dnscrypt_header->magic_query,
66765b390aaSDag-Erling Smørgrav                    DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
668c7f4d7adSDag-Erling Smørgrav 			return &certs[i];
66965b390aaSDag-Erling Smørgrav 		}
67065b390aaSDag-Erling Smørgrav 	}
67165b390aaSDag-Erling Smørgrav 	return NULL;
67265b390aaSDag-Erling Smørgrav }
67365b390aaSDag-Erling Smørgrav 
67465b390aaSDag-Erling Smørgrav /**
67565b390aaSDag-Erling Smørgrav  * Insert local-zone and local-data into configuration.
67665b390aaSDag-Erling Smørgrav  * In order to be able to serve certs over TXT, we can reuse the local-zone and
677*24e36522SCy Schubert  * local-data config option. The zone and qname are inferred from the
67865b390aaSDag-Erling Smørgrav  * provider_name and the content of the TXT record from the certificate content.
6798a384985SDag-Erling Smørgrav  * returns the number of certificate TXT record that were loaded.
68065b390aaSDag-Erling Smørgrav  * < 0 in case of error.
68165b390aaSDag-Erling Smørgrav  */
68265b390aaSDag-Erling Smørgrav static int
dnsc_load_local_data(struct dnsc_env * dnscenv,struct config_file * cfg)68365b390aaSDag-Erling Smørgrav dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg)
68465b390aaSDag-Erling Smørgrav {
68565b390aaSDag-Erling Smørgrav     size_t i, j;
68665b390aaSDag-Erling Smørgrav 	// Insert 'local-zone: "2.dnscrypt-cert.example.com" deny'
68765b390aaSDag-Erling Smørgrav     if(!cfg_str2list_insert(&cfg->local_zones,
68865b390aaSDag-Erling Smørgrav                             strdup(dnscenv->provider_name),
68965b390aaSDag-Erling Smørgrav                             strdup("deny"))) {
69065b390aaSDag-Erling Smørgrav         log_err("Could not load dnscrypt local-zone: %s deny",
69165b390aaSDag-Erling Smørgrav                 dnscenv->provider_name);
69265b390aaSDag-Erling Smørgrav         return -1;
69365b390aaSDag-Erling Smørgrav     }
69465b390aaSDag-Erling Smørgrav 
69565b390aaSDag-Erling Smørgrav     // Add local data entry of type:
69665b390aaSDag-Erling Smørgrav     // 2.dnscrypt-cert.example.com 86400 IN TXT "DNSC......"
69765b390aaSDag-Erling Smørgrav     for(i=0; i<dnscenv->signed_certs_count; i++) {
69865b390aaSDag-Erling Smørgrav         const char *ttl_class_type = " 86400 IN TXT \"";
69957bddd21SDag-Erling Smørgrav         int rotated_cert = 0;
70057bddd21SDag-Erling Smørgrav 	uint32_t serial;
70157bddd21SDag-Erling Smørgrav 	uint16_t rrlen;
70257bddd21SDag-Erling Smørgrav 	char* rr;
70365b390aaSDag-Erling Smørgrav         struct SignedCert *cert = dnscenv->signed_certs + i;
70457bddd21SDag-Erling Smørgrav 		// Check if the certificate is being rotated and should not be published
70557bddd21SDag-Erling Smørgrav         for(j=0; j<dnscenv->rotated_certs_count; j++){
70657bddd21SDag-Erling Smørgrav             if(cert == dnscenv->rotated_certs[j]) {
70757bddd21SDag-Erling Smørgrav                 rotated_cert = 1;
70857bddd21SDag-Erling Smørgrav                 break;
70957bddd21SDag-Erling Smørgrav             }
71057bddd21SDag-Erling Smørgrav         }
71157bddd21SDag-Erling Smørgrav 		memcpy(&serial, cert->serial, sizeof serial);
71257bddd21SDag-Erling Smørgrav 		serial = htonl(serial);
71357bddd21SDag-Erling Smørgrav         if(rotated_cert) {
71457bddd21SDag-Erling Smørgrav             verbose(VERB_OPS,
71557bddd21SDag-Erling Smørgrav                 "DNSCrypt: not adding cert with serial #%"
71657bddd21SDag-Erling Smørgrav                 PRIu32
71757bddd21SDag-Erling Smørgrav                 " to local-data as it is rotated",
71857bddd21SDag-Erling Smørgrav                 serial
71957bddd21SDag-Erling Smørgrav             );
72057bddd21SDag-Erling Smørgrav             continue;
72157bddd21SDag-Erling Smørgrav         }
7220eefd307SCy Schubert 	if((unsigned)strlen(dnscenv->provider_name) >= (unsigned)0xffff0000) {
7230eefd307SCy Schubert 		/* guard against integer overflow in rrlen calculation */
7240eefd307SCy Schubert 		verbose(VERB_OPS, "cert #%" PRIu32 " is too long", serial);
7250eefd307SCy Schubert 		continue;
7260eefd307SCy Schubert 	}
72757bddd21SDag-Erling Smørgrav         rrlen = strlen(dnscenv->provider_name) +
72865b390aaSDag-Erling Smørgrav                          strlen(ttl_class_type) +
72965b390aaSDag-Erling Smørgrav                          4 * sizeof(struct SignedCert) + // worst case scenario
73065b390aaSDag-Erling Smørgrav                          1 + // trailing double quote
73165b390aaSDag-Erling Smørgrav                          1;
73257bddd21SDag-Erling Smørgrav         rr = malloc(rrlen);
73365b390aaSDag-Erling Smørgrav         if(!rr) {
73465b390aaSDag-Erling Smørgrav             log_err("Could not allocate memory");
73565b390aaSDag-Erling Smørgrav             return -2;
73665b390aaSDag-Erling Smørgrav         }
73765b390aaSDag-Erling Smørgrav         snprintf(rr, rrlen - 1, "%s 86400 IN TXT \"", dnscenv->provider_name);
73865b390aaSDag-Erling Smørgrav         for(j=0; j<sizeof(struct SignedCert); j++) {
73965b390aaSDag-Erling Smørgrav 			int c = (int)*((const uint8_t *) cert + j);
74065b390aaSDag-Erling Smørgrav             if (isprint(c) && c != '"' && c != '\\') {
7410eefd307SCy Schubert                 snprintf(rr + strlen(rr), rrlen - strlen(rr), "%c", c);
74265b390aaSDag-Erling Smørgrav             } else {
7430eefd307SCy Schubert                 snprintf(rr + strlen(rr), rrlen - strlen(rr), "\\%03d", c);
74465b390aaSDag-Erling Smørgrav             }
74565b390aaSDag-Erling Smørgrav         }
74657bddd21SDag-Erling Smørgrav         verbose(VERB_OPS,
74757bddd21SDag-Erling Smørgrav 			"DNSCrypt: adding cert with serial #%"
74857bddd21SDag-Erling Smørgrav 			PRIu32
74957bddd21SDag-Erling Smørgrav 			" to local-data to config: %s",
75057bddd21SDag-Erling Smørgrav 			serial, rr
75157bddd21SDag-Erling Smørgrav 		);
7520eefd307SCy Schubert         snprintf(rr + strlen(rr), rrlen - strlen(rr), "\"");
75365b390aaSDag-Erling Smørgrav         cfg_strlist_insert(&cfg->local_data, strdup(rr));
75465b390aaSDag-Erling Smørgrav         free(rr);
75565b390aaSDag-Erling Smørgrav     }
75665b390aaSDag-Erling Smørgrav     return dnscenv->signed_certs_count;
75765b390aaSDag-Erling Smørgrav }
75865b390aaSDag-Erling Smørgrav 
759c7f4d7adSDag-Erling Smørgrav static const char *
key_get_es_version(uint8_t version[2])760c7f4d7adSDag-Erling Smørgrav key_get_es_version(uint8_t version[2])
761c7f4d7adSDag-Erling Smørgrav {
762c7f4d7adSDag-Erling Smørgrav     struct es_version {
763c7f4d7adSDag-Erling Smørgrav         uint8_t es_version[2];
764c7f4d7adSDag-Erling Smørgrav         const char *name;
765c7f4d7adSDag-Erling Smørgrav     };
766c7f4d7adSDag-Erling Smørgrav 
767e86b9096SDag-Erling Smørgrav     const int num_versions = 2;
768c7f4d7adSDag-Erling Smørgrav     struct es_version es_versions[] = {
769c7f4d7adSDag-Erling Smørgrav         {{0x00, 0x01}, "X25519-XSalsa20Poly1305"},
770c7f4d7adSDag-Erling Smørgrav         {{0x00, 0x02}, "X25519-XChacha20Poly1305"},
771c7f4d7adSDag-Erling Smørgrav     };
772c7f4d7adSDag-Erling Smørgrav     int i;
773e86b9096SDag-Erling Smørgrav     for(i=0; i < num_versions; i++){
774c7f4d7adSDag-Erling Smørgrav         if(es_versions[i].es_version[0] == version[0] &&
775c7f4d7adSDag-Erling Smørgrav            es_versions[i].es_version[1] == version[1]){
776c7f4d7adSDag-Erling Smørgrav             return es_versions[i].name;
777c7f4d7adSDag-Erling Smørgrav         }
778c7f4d7adSDag-Erling Smørgrav     }
779c7f4d7adSDag-Erling Smørgrav     return NULL;
780c7f4d7adSDag-Erling Smørgrav }
781c7f4d7adSDag-Erling Smørgrav 
782c7f4d7adSDag-Erling Smørgrav 
78365b390aaSDag-Erling Smørgrav /**
78465b390aaSDag-Erling Smørgrav  * Parse the secret key files from `dnscrypt-secret-key` config and populates
785c7f4d7adSDag-Erling Smørgrav  * a list of dnsccert with es_version, magic number and secret/public keys
786c7f4d7adSDag-Erling Smørgrav  * supported by dnscrypt listener.
78765b390aaSDag-Erling Smørgrav  * \param[in] env The dnsc_env structure which will hold the keypairs.
78865b390aaSDag-Erling Smørgrav  * \param[in] cfg The config with the secret key file paths.
78965b390aaSDag-Erling Smørgrav  */
79065b390aaSDag-Erling Smørgrav static int
dnsc_parse_keys(struct dnsc_env * env,struct config_file * cfg)79165b390aaSDag-Erling Smørgrav dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg)
79265b390aaSDag-Erling Smørgrav {
79365b390aaSDag-Erling Smørgrav 	struct config_strlist *head;
794c7f4d7adSDag-Erling Smørgrav 	size_t cert_id, keypair_id;
795c7f4d7adSDag-Erling Smørgrav 	size_t c;
796c7f4d7adSDag-Erling Smørgrav 	char *nm;
79765b390aaSDag-Erling Smørgrav 
79865b390aaSDag-Erling Smørgrav 	env->keypairs_count = 0U;
79965b390aaSDag-Erling Smørgrav 	for (head = cfg->dnscrypt_secret_key; head; head = head->next) {
80065b390aaSDag-Erling Smørgrav 		env->keypairs_count++;
80165b390aaSDag-Erling Smørgrav 	}
802c7f4d7adSDag-Erling Smørgrav 
80365b390aaSDag-Erling Smørgrav 	env->keypairs = sodium_allocarray(env->keypairs_count,
80465b390aaSDag-Erling Smørgrav 		sizeof *env->keypairs);
805c7f4d7adSDag-Erling Smørgrav 	env->certs = sodium_allocarray(env->signed_certs_count,
806c7f4d7adSDag-Erling Smørgrav 		sizeof *env->certs);
80765b390aaSDag-Erling Smørgrav 
808c7f4d7adSDag-Erling Smørgrav 	cert_id = 0U;
80965b390aaSDag-Erling Smørgrav 	keypair_id = 0U;
81065b390aaSDag-Erling Smørgrav 	for(head = cfg->dnscrypt_secret_key; head; head = head->next, keypair_id++) {
81165b390aaSDag-Erling Smørgrav 		char fingerprint[80];
812c7f4d7adSDag-Erling Smørgrav 		int found_cert = 0;
813c7f4d7adSDag-Erling Smørgrav 		KeyPair *current_keypair = &env->keypairs[keypair_id];
814c7f4d7adSDag-Erling Smørgrav 		nm = dnsc_chroot_path(cfg, head->str);
81565b390aaSDag-Erling Smørgrav 		if(dnsc_read_from_file(
816c7f4d7adSDag-Erling Smørgrav 				nm,
817c7f4d7adSDag-Erling Smørgrav 				(char *)(current_keypair->crypt_secretkey),
81865b390aaSDag-Erling Smørgrav 				crypto_box_SECRETKEYBYTES) != 0) {
81965b390aaSDag-Erling Smørgrav 			fatal_exit("dnsc_parse_keys: failed to load %s: %s", head->str, strerror(errno));
82065b390aaSDag-Erling Smørgrav 		}
82165b390aaSDag-Erling Smørgrav 		verbose(VERB_OPS, "Loaded key %s", head->str);
822c7f4d7adSDag-Erling Smørgrav 		if (crypto_scalarmult_base(current_keypair->crypt_publickey,
823c7f4d7adSDag-Erling Smørgrav 			current_keypair->crypt_secretkey) != 0) {
82465b390aaSDag-Erling Smørgrav 			fatal_exit("dnsc_parse_keys: could not generate public key from %s", head->str);
82565b390aaSDag-Erling Smørgrav 		}
826c7f4d7adSDag-Erling Smørgrav 		dnsc_key_to_fingerprint(fingerprint, current_keypair->crypt_publickey);
82765b390aaSDag-Erling Smørgrav 		verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s", head->str, fingerprint);
828c7f4d7adSDag-Erling Smørgrav 		// find the cert matching this key
829c7f4d7adSDag-Erling Smørgrav 		for(c = 0; c < env->signed_certs_count; c++) {
830c7f4d7adSDag-Erling Smørgrav 			if(memcmp(current_keypair->crypt_publickey,
831c7f4d7adSDag-Erling Smørgrav 				env->signed_certs[c].server_publickey,
832c7f4d7adSDag-Erling Smørgrav 				crypto_box_PUBLICKEYBYTES) == 0) {
833c7f4d7adSDag-Erling Smørgrav 				dnsccert *current_cert = &env->certs[cert_id++];
834c7f4d7adSDag-Erling Smørgrav 				found_cert = 1;
835c7f4d7adSDag-Erling Smørgrav 				current_cert->keypair = current_keypair;
836c7f4d7adSDag-Erling Smørgrav 				memcpy(current_cert->magic_query,
837c7f4d7adSDag-Erling Smørgrav 				       env->signed_certs[c].magic_query,
838c7f4d7adSDag-Erling Smørgrav 					sizeof env->signed_certs[c].magic_query);
839c7f4d7adSDag-Erling Smørgrav 				memcpy(current_cert->es_version,
840c7f4d7adSDag-Erling Smørgrav 				       env->signed_certs[c].version_major,
841c7f4d7adSDag-Erling Smørgrav 				       sizeof env->signed_certs[c].version_major
842c7f4d7adSDag-Erling Smørgrav 				);
843c7f4d7adSDag-Erling Smørgrav 				dnsc_key_to_fingerprint(fingerprint,
844c7f4d7adSDag-Erling Smørgrav 							current_cert->keypair->crypt_publickey);
845c7f4d7adSDag-Erling Smørgrav 				verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s",
846c7f4d7adSDag-Erling Smørgrav 					head->str, fingerprint);
847c7f4d7adSDag-Erling Smørgrav 				verbose(VERB_OPS, "Using %s",
848c7f4d7adSDag-Erling Smørgrav 					key_get_es_version(current_cert->es_version));
849c7f4d7adSDag-Erling Smørgrav #ifndef USE_DNSCRYPT_XCHACHA20
850c7f4d7adSDag-Erling Smørgrav 				if (current_cert->es_version[1] == 0x02) {
851c7f4d7adSDag-Erling Smørgrav 				    fatal_exit("Certificate for XChacha20 but libsodium does not support it.");
85265b390aaSDag-Erling Smørgrav 				}
853c7f4d7adSDag-Erling Smørgrav #endif
854c7f4d7adSDag-Erling Smørgrav 
855c7f4d7adSDag-Erling Smørgrav             		}
856c7f4d7adSDag-Erling Smørgrav         	}
857c7f4d7adSDag-Erling Smørgrav 		if (!found_cert) {
858c7f4d7adSDag-Erling Smørgrav 		    fatal_exit("dnsc_parse_keys: could not match certificate for key "
859c7f4d7adSDag-Erling Smørgrav 			       "%s. Unable to determine ES version.",
860c7f4d7adSDag-Erling Smørgrav 			       head->str);
861c7f4d7adSDag-Erling Smørgrav 		}
862c7f4d7adSDag-Erling Smørgrav 	}
863c7f4d7adSDag-Erling Smørgrav 	return cert_id;
86465b390aaSDag-Erling Smørgrav }
86565b390aaSDag-Erling Smørgrav 
8665469a995SCy Schubert #ifdef SODIUM_MISUSE_HANDLER
86757bddd21SDag-Erling Smørgrav static void
sodium_misuse_handler(void)86857bddd21SDag-Erling Smørgrav sodium_misuse_handler(void)
86957bddd21SDag-Erling Smørgrav {
87057bddd21SDag-Erling Smørgrav 	fatal_exit(
87157bddd21SDag-Erling Smørgrav 		"dnscrypt: libsodium could not be initialized, this typically"
87257bddd21SDag-Erling Smørgrav 		" happens when no good source of entropy is found. If you run"
8730eefd307SCy Schubert 		" unbound in a chroot, make sure /dev/urandom is available. See"
87457bddd21SDag-Erling Smørgrav 		" https://www.unbound.net/documentation/unbound.conf.html");
87557bddd21SDag-Erling Smørgrav }
8765469a995SCy Schubert #endif
87757bddd21SDag-Erling Smørgrav 
87865b390aaSDag-Erling Smørgrav 
87965b390aaSDag-Erling Smørgrav /**
88065b390aaSDag-Erling Smørgrav  * #########################################################
88165b390aaSDag-Erling Smørgrav  * ############# Publicly accessible functions #############
88265b390aaSDag-Erling Smørgrav  * #########################################################
88365b390aaSDag-Erling Smørgrav  */
88465b390aaSDag-Erling Smørgrav 
88565b390aaSDag-Erling Smørgrav int
dnsc_handle_curved_request(struct dnsc_env * dnscenv,struct comm_reply * repinfo)88665b390aaSDag-Erling Smørgrav dnsc_handle_curved_request(struct dnsc_env* dnscenv,
88765b390aaSDag-Erling Smørgrav                            struct comm_reply* repinfo)
88865b390aaSDag-Erling Smørgrav {
88965b390aaSDag-Erling Smørgrav     struct comm_point* c = repinfo->c;
89065b390aaSDag-Erling Smørgrav 
89165b390aaSDag-Erling Smørgrav     repinfo->is_dnscrypted = 0;
89265b390aaSDag-Erling Smørgrav     if( !c->dnscrypt ) {
89365b390aaSDag-Erling Smørgrav         return 1;
89465b390aaSDag-Erling Smørgrav     }
89565b390aaSDag-Erling Smørgrav     // Attempt to decrypt the query. If it is not crypted, we may still need
89665b390aaSDag-Erling Smørgrav     // to serve the certificate.
89765b390aaSDag-Erling Smørgrav     verbose(VERB_ALGO, "handle request called on DNSCrypt socket");
898c7f4d7adSDag-Erling Smørgrav     if ((repinfo->dnsc_cert = dnsc_find_cert(dnscenv, c->buffer)) != NULL) {
899971980c3SDag-Erling Smørgrav         if(dnscrypt_server_uncurve(dnscenv,
900971980c3SDag-Erling Smørgrav                                    repinfo->dnsc_cert,
90165b390aaSDag-Erling Smørgrav                                    repinfo->client_nonce,
90265b390aaSDag-Erling Smørgrav                                    repinfo->nmkey,
90365b390aaSDag-Erling Smørgrav                                    c->buffer) != 0){
90465b390aaSDag-Erling Smørgrav             verbose(VERB_ALGO, "dnscrypt: Failed to uncurve");
90565b390aaSDag-Erling Smørgrav             comm_point_drop_reply(repinfo);
90665b390aaSDag-Erling Smørgrav             return 0;
90765b390aaSDag-Erling Smørgrav         }
90865b390aaSDag-Erling Smørgrav         repinfo->is_dnscrypted = 1;
90965b390aaSDag-Erling Smørgrav         sldns_buffer_rewind(c->buffer);
91065b390aaSDag-Erling Smørgrav     }
91165b390aaSDag-Erling Smørgrav     return 1;
91265b390aaSDag-Erling Smørgrav }
91365b390aaSDag-Erling Smørgrav 
91465b390aaSDag-Erling Smørgrav int
dnsc_handle_uncurved_request(struct comm_reply * repinfo)91565b390aaSDag-Erling Smørgrav dnsc_handle_uncurved_request(struct comm_reply *repinfo)
91665b390aaSDag-Erling Smørgrav {
91765b390aaSDag-Erling Smørgrav     if(!repinfo->c->dnscrypt) {
91865b390aaSDag-Erling Smørgrav         return 1;
91965b390aaSDag-Erling Smørgrav     }
92065b390aaSDag-Erling Smørgrav     sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer);
92165b390aaSDag-Erling Smørgrav     if(!repinfo->is_dnscrypted) {
92265b390aaSDag-Erling Smørgrav         return 1;
92365b390aaSDag-Erling Smørgrav     }
924c7f4d7adSDag-Erling Smørgrav 	if(dnscrypt_server_curve(repinfo->dnsc_cert,
92565b390aaSDag-Erling Smørgrav                              repinfo->client_nonce,
92665b390aaSDag-Erling Smørgrav                              repinfo->nmkey,
92765b390aaSDag-Erling Smørgrav                              repinfo->c->dnscrypt_buffer,
92865b390aaSDag-Erling Smørgrav                              repinfo->c->type == comm_udp,
92965b390aaSDag-Erling Smørgrav                              repinfo->max_udp_size) != 0){
93065b390aaSDag-Erling Smørgrav 		verbose(VERB_ALGO, "dnscrypt: Failed to curve cached missed answer");
93165b390aaSDag-Erling Smørgrav 		comm_point_drop_reply(repinfo);
93265b390aaSDag-Erling Smørgrav 		return 0;
93365b390aaSDag-Erling Smørgrav 	}
93465b390aaSDag-Erling Smørgrav     return 1;
93565b390aaSDag-Erling Smørgrav }
93665b390aaSDag-Erling Smørgrav 
93765b390aaSDag-Erling Smørgrav struct dnsc_env *
dnsc_create(void)93865b390aaSDag-Erling Smørgrav dnsc_create(void)
93965b390aaSDag-Erling Smørgrav {
94065b390aaSDag-Erling Smørgrav 	struct dnsc_env *env;
94157bddd21SDag-Erling Smørgrav #ifdef SODIUM_MISUSE_HANDLER
94257bddd21SDag-Erling Smørgrav 	sodium_set_misuse_handler(sodium_misuse_handler);
94357bddd21SDag-Erling Smørgrav #endif
94465b390aaSDag-Erling Smørgrav 	if (sodium_init() == -1) {
94565b390aaSDag-Erling Smørgrav 		fatal_exit("dnsc_create: could not initialize libsodium.");
94665b390aaSDag-Erling Smørgrav 	}
94765b390aaSDag-Erling Smørgrav 	env = (struct dnsc_env *) calloc(1, sizeof(struct dnsc_env));
948971980c3SDag-Erling Smørgrav 	lock_basic_init(&env->shared_secrets_cache_lock);
949971980c3SDag-Erling Smørgrav 	lock_protect(&env->shared_secrets_cache_lock,
950971980c3SDag-Erling Smørgrav                  &env->num_query_dnscrypt_secret_missed_cache,
951971980c3SDag-Erling Smørgrav                  sizeof(env->num_query_dnscrypt_secret_missed_cache));
9528a384985SDag-Erling Smørgrav 	lock_basic_init(&env->nonces_cache_lock);
9538a384985SDag-Erling Smørgrav 	lock_protect(&env->nonces_cache_lock,
9548a384985SDag-Erling Smørgrav                  &env->nonces_cache,
9558a384985SDag-Erling Smørgrav                  sizeof(env->nonces_cache));
9568a384985SDag-Erling Smørgrav 	lock_protect(&env->nonces_cache_lock,
9578a384985SDag-Erling Smørgrav                  &env->num_query_dnscrypt_replay,
9588a384985SDag-Erling Smørgrav                  sizeof(env->num_query_dnscrypt_replay));
9598a384985SDag-Erling Smørgrav 
96065b390aaSDag-Erling Smørgrav 	return env;
96165b390aaSDag-Erling Smørgrav }
96265b390aaSDag-Erling Smørgrav 
96365b390aaSDag-Erling Smørgrav int
dnsc_apply_cfg(struct dnsc_env * env,struct config_file * cfg)96465b390aaSDag-Erling Smørgrav dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg)
96565b390aaSDag-Erling Smørgrav {
96665b390aaSDag-Erling Smørgrav     if(dnsc_parse_certs(env, cfg) <= 0) {
96765b390aaSDag-Erling Smørgrav         fatal_exit("dnsc_apply_cfg: no cert file loaded");
96865b390aaSDag-Erling Smørgrav     }
96965b390aaSDag-Erling Smørgrav     if(dnsc_parse_keys(env, cfg) <= 0) {
97065b390aaSDag-Erling Smørgrav         fatal_exit("dnsc_apply_cfg: no key file loaded");
97165b390aaSDag-Erling Smørgrav     }
97265b390aaSDag-Erling Smørgrav     randombytes_buf(env->hash_key, sizeof env->hash_key);
97365b390aaSDag-Erling Smørgrav     env->provider_name = cfg->dnscrypt_provider;
97465b390aaSDag-Erling Smørgrav 
97565b390aaSDag-Erling Smørgrav     if(dnsc_load_local_data(env, cfg) <= 0) {
97665b390aaSDag-Erling Smørgrav         fatal_exit("dnsc_apply_cfg: could not load local data");
97765b390aaSDag-Erling Smørgrav     }
97857bddd21SDag-Erling Smørgrav     lock_basic_lock(&env->shared_secrets_cache_lock);
979971980c3SDag-Erling Smørgrav     env->shared_secrets_cache = slabhash_create(
980971980c3SDag-Erling Smørgrav         cfg->dnscrypt_shared_secret_cache_slabs,
981971980c3SDag-Erling Smørgrav         HASH_DEFAULT_STARTARRAY,
982971980c3SDag-Erling Smørgrav         cfg->dnscrypt_shared_secret_cache_size,
983971980c3SDag-Erling Smørgrav         dnsc_shared_secrets_sizefunc,
984971980c3SDag-Erling Smørgrav         dnsc_shared_secrets_compfunc,
985971980c3SDag-Erling Smørgrav         dnsc_shared_secrets_delkeyfunc,
986971980c3SDag-Erling Smørgrav         dnsc_shared_secrets_deldatafunc,
987971980c3SDag-Erling Smørgrav         NULL
988971980c3SDag-Erling Smørgrav     );
98957bddd21SDag-Erling Smørgrav     lock_basic_unlock(&env->shared_secrets_cache_lock);
990971980c3SDag-Erling Smørgrav     if(!env->shared_secrets_cache){
991971980c3SDag-Erling Smørgrav         fatal_exit("dnsc_apply_cfg: could not create shared secrets cache.");
992971980c3SDag-Erling Smørgrav     }
99357bddd21SDag-Erling Smørgrav     lock_basic_lock(&env->nonces_cache_lock);
9948a384985SDag-Erling Smørgrav     env->nonces_cache = slabhash_create(
9958a384985SDag-Erling Smørgrav         cfg->dnscrypt_nonce_cache_slabs,
9968a384985SDag-Erling Smørgrav         HASH_DEFAULT_STARTARRAY,
9978a384985SDag-Erling Smørgrav         cfg->dnscrypt_nonce_cache_size,
9988a384985SDag-Erling Smørgrav         dnsc_nonces_sizefunc,
9998a384985SDag-Erling Smørgrav         dnsc_nonces_compfunc,
10008a384985SDag-Erling Smørgrav         dnsc_nonces_delkeyfunc,
10018a384985SDag-Erling Smørgrav         dnsc_nonces_deldatafunc,
10028a384985SDag-Erling Smørgrav         NULL
10038a384985SDag-Erling Smørgrav     );
100457bddd21SDag-Erling Smørgrav     lock_basic_unlock(&env->nonces_cache_lock);
100565b390aaSDag-Erling Smørgrav     return 0;
100665b390aaSDag-Erling Smørgrav }
1007971980c3SDag-Erling Smørgrav 
1008971980c3SDag-Erling Smørgrav void
dnsc_delete(struct dnsc_env * env)1009971980c3SDag-Erling Smørgrav dnsc_delete(struct dnsc_env *env)
1010971980c3SDag-Erling Smørgrav {
1011971980c3SDag-Erling Smørgrav 	if(!env) {
1012971980c3SDag-Erling Smørgrav 		return;
1013971980c3SDag-Erling Smørgrav 	}
1014971980c3SDag-Erling Smørgrav 	verbose(VERB_OPS, "DNSCrypt: Freeing environment.");
1015971980c3SDag-Erling Smørgrav 	sodium_free(env->signed_certs);
101657bddd21SDag-Erling Smørgrav 	sodium_free(env->rotated_certs);
1017971980c3SDag-Erling Smørgrav 	sodium_free(env->certs);
1018971980c3SDag-Erling Smørgrav 	sodium_free(env->keypairs);
1019971980c3SDag-Erling Smørgrav 	lock_basic_destroy(&env->shared_secrets_cache_lock);
10208a384985SDag-Erling Smørgrav 	lock_basic_destroy(&env->nonces_cache_lock);
102157bddd21SDag-Erling Smørgrav 	slabhash_delete(env->shared_secrets_cache);
102257bddd21SDag-Erling Smørgrav 	slabhash_delete(env->nonces_cache);
1023971980c3SDag-Erling Smørgrav 	free(env);
1024971980c3SDag-Erling Smørgrav }
1025971980c3SDag-Erling Smørgrav 
1026971980c3SDag-Erling Smørgrav /**
1027971980c3SDag-Erling Smørgrav  * #########################################################
1028971980c3SDag-Erling Smørgrav  * ############# Shared secrets cache functions ############
1029971980c3SDag-Erling Smørgrav  * #########################################################
1030971980c3SDag-Erling Smørgrav  */
1031971980c3SDag-Erling Smørgrav 
1032971980c3SDag-Erling Smørgrav size_t
dnsc_shared_secrets_sizefunc(void * k,void * ATTR_UNUSED (d))1033971980c3SDag-Erling Smørgrav dnsc_shared_secrets_sizefunc(void *k, void* ATTR_UNUSED(d))
1034971980c3SDag-Erling Smørgrav {
1035971980c3SDag-Erling Smørgrav     struct shared_secret_cache_key* ssk = (struct shared_secret_cache_key*)k;
1036971980c3SDag-Erling Smørgrav     size_t key_size = sizeof(struct shared_secret_cache_key)
1037971980c3SDag-Erling Smørgrav         + lock_get_mem(&ssk->entry.lock);
1038971980c3SDag-Erling Smørgrav     size_t data_size = crypto_box_BEFORENMBYTES;
1039971980c3SDag-Erling Smørgrav     (void)ssk; /* otherwise ssk is unused if no threading, or fixed locksize */
1040971980c3SDag-Erling Smørgrav     return key_size + data_size;
1041971980c3SDag-Erling Smørgrav }
1042971980c3SDag-Erling Smørgrav 
1043971980c3SDag-Erling Smørgrav int
dnsc_shared_secrets_compfunc(void * m1,void * m2)1044971980c3SDag-Erling Smørgrav dnsc_shared_secrets_compfunc(void *m1, void *m2)
1045971980c3SDag-Erling Smørgrav {
1046971980c3SDag-Erling Smørgrav     return sodium_memcmp(m1, m2, DNSCRYPT_SHARED_SECRET_KEY_LENGTH);
1047971980c3SDag-Erling Smørgrav }
1048971980c3SDag-Erling Smørgrav 
1049971980c3SDag-Erling Smørgrav void
dnsc_shared_secrets_delkeyfunc(void * k,void * ATTR_UNUSED (arg))1050971980c3SDag-Erling Smørgrav dnsc_shared_secrets_delkeyfunc(void *k, void* ATTR_UNUSED(arg))
1051971980c3SDag-Erling Smørgrav {
1052971980c3SDag-Erling Smørgrav     struct shared_secret_cache_key* ssk = (struct shared_secret_cache_key*)k;
1053971980c3SDag-Erling Smørgrav     lock_rw_destroy(&ssk->entry.lock);
1054971980c3SDag-Erling Smørgrav     free(ssk);
1055971980c3SDag-Erling Smørgrav }
1056971980c3SDag-Erling Smørgrav 
1057971980c3SDag-Erling Smørgrav void
dnsc_shared_secrets_deldatafunc(void * d,void * ATTR_UNUSED (arg))1058971980c3SDag-Erling Smørgrav dnsc_shared_secrets_deldatafunc(void* d, void* ATTR_UNUSED(arg))
1059971980c3SDag-Erling Smørgrav {
1060971980c3SDag-Erling Smørgrav     uint8_t* data = (uint8_t*)d;
1061971980c3SDag-Erling Smørgrav     free(data);
1062971980c3SDag-Erling Smørgrav }
10638a384985SDag-Erling Smørgrav 
10648a384985SDag-Erling Smørgrav /**
10658a384985SDag-Erling Smørgrav  * #########################################################
10668a384985SDag-Erling Smørgrav  * ############### Nonces cache functions ##################
10678a384985SDag-Erling Smørgrav  * #########################################################
10688a384985SDag-Erling Smørgrav  */
10698a384985SDag-Erling Smørgrav 
10708a384985SDag-Erling Smørgrav size_t
dnsc_nonces_sizefunc(void * k,void * ATTR_UNUSED (d))10718a384985SDag-Erling Smørgrav dnsc_nonces_sizefunc(void *k, void* ATTR_UNUSED(d))
10728a384985SDag-Erling Smørgrav {
10738a384985SDag-Erling Smørgrav     struct nonce_cache_key* nk = (struct nonce_cache_key*)k;
10748a384985SDag-Erling Smørgrav     size_t key_size = sizeof(struct nonce_cache_key)
10758a384985SDag-Erling Smørgrav         + lock_get_mem(&nk->entry.lock);
10768a384985SDag-Erling Smørgrav     (void)nk; /* otherwise ssk is unused if no threading, or fixed locksize */
10778a384985SDag-Erling Smørgrav     return key_size;
10788a384985SDag-Erling Smørgrav }
10798a384985SDag-Erling Smørgrav 
10808a384985SDag-Erling Smørgrav int
dnsc_nonces_compfunc(void * m1,void * m2)10818a384985SDag-Erling Smørgrav dnsc_nonces_compfunc(void *m1, void *m2)
10828a384985SDag-Erling Smørgrav {
10838a384985SDag-Erling Smørgrav     struct nonce_cache_key *k1 = m1, *k2 = m2;
10848a384985SDag-Erling Smørgrav     return
10858a384985SDag-Erling Smørgrav         sodium_memcmp(
10868a384985SDag-Erling Smørgrav             k1->nonce,
10878a384985SDag-Erling Smørgrav             k2->nonce,
10888a384985SDag-Erling Smørgrav             crypto_box_HALF_NONCEBYTES) != 0 ||
10898a384985SDag-Erling Smørgrav         sodium_memcmp(
10908a384985SDag-Erling Smørgrav             k1->magic_query,
10918a384985SDag-Erling Smørgrav             k2->magic_query,
10928a384985SDag-Erling Smørgrav             DNSCRYPT_MAGIC_HEADER_LEN) != 0 ||
10938a384985SDag-Erling Smørgrav         sodium_memcmp(
10948a384985SDag-Erling Smørgrav             k1->client_publickey, k2->client_publickey,
10958a384985SDag-Erling Smørgrav             crypto_box_PUBLICKEYBYTES) != 0;
10968a384985SDag-Erling Smørgrav }
10978a384985SDag-Erling Smørgrav 
10988a384985SDag-Erling Smørgrav void
dnsc_nonces_delkeyfunc(void * k,void * ATTR_UNUSED (arg))10998a384985SDag-Erling Smørgrav dnsc_nonces_delkeyfunc(void *k, void* ATTR_UNUSED(arg))
11008a384985SDag-Erling Smørgrav {
11018a384985SDag-Erling Smørgrav     struct nonce_cache_key* nk = (struct nonce_cache_key*)k;
11028a384985SDag-Erling Smørgrav     lock_rw_destroy(&nk->entry.lock);
11038a384985SDag-Erling Smørgrav     free(nk);
11048a384985SDag-Erling Smørgrav }
11058a384985SDag-Erling Smørgrav 
11068a384985SDag-Erling Smørgrav void
dnsc_nonces_deldatafunc(void * ATTR_UNUSED (d),void * ATTR_UNUSED (arg))11078a384985SDag-Erling Smørgrav dnsc_nonces_deldatafunc(void* ATTR_UNUSED(d), void* ATTR_UNUSED(arg))
11088a384985SDag-Erling Smørgrav {
11098a384985SDag-Erling Smørgrav     return;
11108a384985SDag-Erling Smørgrav }
1111