resetting manifest requested domain to floor
[platform/upstream/crda.git] / reglib.c
1 #include <errno.h>
2 #include <stdio.h>
3 #include <arpa/inet.h>
4 #include <sys/types.h>
5 #include <dirent.h>
6 #include <sys/stat.h>
7 #include <stdlib.h>
8 #include <sys/mman.h>
9 #include <fcntl.h>
10 #include <stdbool.h>
11 #include <unistd.h>
12 #include <string.h>
13
14 #include <arpa/inet.h> /* ntohl */
15
16 #include "reglib.h"
17 #include "regdb.h"
18
19 #ifdef USE_OPENSSL
20 #include <openssl/objects.h>
21 #include <openssl/rsa.h>
22 #include <openssl/sha.h>
23 #include <openssl/pem.h>
24 #endif
25
26 #ifdef USE_GCRYPT
27 #include <gcrypt.h>
28 #endif
29
30 #include "reglib.h"
31
32 #ifdef USE_OPENSSL
33 #include "keys-ssl.c"
34 #endif
35
36 #ifdef USE_GCRYPT
37 #include "keys-gcrypt.c"
38 #endif
39
40 void *crda_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
41 {
42         uint32_t p = ntohl(ptr);
43
44         if (p > dblen - structlen) {
45                 fprintf(stderr, "Invalid database file, bad pointer!\n");
46                 exit(3);
47         }
48
49         return (void *)(db + p);
50 }
51
52 /*
53  * Checks the validity of the signature found on the regulatory
54  * database against the array 'keys'. Returns 1 if there exists
55  * at least one key in the array such that the signature is valid
56  * against that key; 0 otherwise.
57  */
58 int crda_verify_db_signature(uint8_t *db, int dblen, int siglen)
59 {
60 #ifdef USE_OPENSSL
61         RSA *rsa;
62         uint8_t hash[SHA_DIGEST_LENGTH];
63         unsigned int i;
64         int ok = 0;
65         DIR *pubkey_dir;
66         struct dirent *nextfile;
67         FILE *keyfile;
68         char filename[PATH_MAX];
69
70         if (SHA1(db, dblen, hash) != hash) {
71                 fprintf(stderr, "Failed to calculate SHA1 sum.\n");
72                 goto out;
73         }
74
75         for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
76                 rsa = RSA_new();
77                 if (!rsa) {
78                         fprintf(stderr, "Failed to create RSA key.\n");
79                         goto out;
80                 }
81
82                 rsa->e = &keys[i].e;
83                 rsa->n = &keys[i].n;
84
85                 ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
86                                 db + dblen, siglen, rsa) == 1;
87
88                 rsa->e = NULL;
89                 rsa->n = NULL;
90                 RSA_free(rsa);
91         }
92         if (!ok && (pubkey_dir = opendir(PUBKEY_DIR))) {
93                 while (!ok && (nextfile = readdir(pubkey_dir))) {
94                         snprintf(filename, PATH_MAX, "%s/%s", PUBKEY_DIR,
95                                 nextfile->d_name);
96                         if ((keyfile = fopen(filename, "rb"))) {
97                                 rsa = PEM_read_RSA_PUBKEY(keyfile,
98                                         NULL, NULL, NULL);
99                                 if (rsa)
100                                         ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
101                                                 db + dblen, siglen, rsa) == 1;
102                                 RSA_free(rsa);
103                                 fclose(keyfile);
104                         }
105                 }
106                 closedir(pubkey_dir);
107         }
108 #endif
109
110 #ifdef USE_GCRYPT
111         gcry_mpi_t mpi_e, mpi_n;
112         gcry_sexp_t rsa, signature, data;
113         uint8_t hash[20];
114         unsigned int i;
115         int ok = 0;
116
117         /* initialise */
118         gcry_check_version(NULL);
119
120         /* hash the db */
121         gcry_md_hash_buffer(GCRY_MD_SHA1, hash, db, dblen);
122
123         if (gcry_sexp_build(&data, NULL, "(data (flags pkcs1) (hash sha1 %b))",
124                             20, hash)) {
125                 fprintf(stderr, "Failed to build data S-expression.\n");
126                 goto out;
127         }
128
129         if (gcry_sexp_build(&signature, NULL, "(sig-val (rsa (s %b)))",
130                             siglen, db + dblen)) {
131                 fprintf(stderr, "Failed to build signature S-expression.\n");
132                 goto out;
133         }
134
135         for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
136                 if (gcry_mpi_scan(&mpi_e, GCRYMPI_FMT_USG,
137                                 keys[i].e, keys[i].len_e, NULL) ||
138                     gcry_mpi_scan(&mpi_n, GCRYMPI_FMT_USG,
139                                 keys[i].n, keys[i].len_n, NULL)) {
140                         fprintf(stderr, "Failed to convert numbers.\n");
141                         goto out;
142                 }
143
144                 if (gcry_sexp_build(&rsa, NULL,
145                                     "(public-key (rsa (n %m) (e %m)))",
146                                     mpi_n, mpi_e)) {
147                         fprintf(stderr, "Failed to build RSA S-expression.\n");
148                         goto out;
149                 }
150
151                 ok = gcry_pk_verify(signature, data, rsa) == 0;
152         }
153 #endif
154
155 #if defined(USE_OPENSSL) || defined(USE_GCRYPT)
156         if (!ok)
157                 fprintf(stderr, "Database signature verification failed.\n");
158
159 out:
160         return ok;
161 #else
162         return 1;
163 #endif
164 }
165
166 static void reg_rule2rd(uint8_t *db, int dblen,
167         uint32_t ruleptr, struct ieee80211_reg_rule *rd_reg_rule)
168 {
169         struct regdb_file_reg_rule *rule;
170         struct regdb_file_freq_range *freq;
171         struct regdb_file_power_rule *power;
172
173         struct ieee80211_freq_range *rd_freq_range = &rd_reg_rule->freq_range;
174         struct ieee80211_power_rule *rd_power_rule = &rd_reg_rule->power_rule;
175
176         rule  = crda_get_file_ptr(db, dblen, sizeof(*rule), ruleptr);
177         freq  = crda_get_file_ptr(db, dblen, sizeof(*freq), rule->freq_range_ptr);
178         power = crda_get_file_ptr(db, dblen, sizeof(*power), rule->power_rule_ptr);
179
180         rd_freq_range->start_freq_khz = ntohl(freq->start_freq);
181         rd_freq_range->end_freq_khz = ntohl(freq->end_freq);
182         rd_freq_range->max_bandwidth_khz = ntohl(freq->max_bandwidth);
183
184         rd_power_rule->max_antenna_gain = ntohl(power->max_antenna_gain);
185         rd_power_rule->max_eirp = ntohl(power->max_eirp);
186
187         rd_reg_rule->flags = ntohl(rule->flags);
188 }
189
190 /* Converts a file regdomain to ieee80211_regdomain, easier to manage */
191 static struct ieee80211_regdomain *
192 country2rd(uint8_t *db, int dblen,
193            struct regdb_file_reg_country *country)
194 {
195         struct regdb_file_reg_rules_collection *rcoll;
196         struct ieee80211_regdomain *rd;
197         int i, num_rules, size_of_rd;
198
199         rcoll = crda_get_file_ptr(db, dblen, sizeof(*rcoll),
200                                 country->reg_collection_ptr);
201         num_rules = ntohl(rcoll->reg_rule_num);
202         /* re-get pointer with sanity checking for num_rules */
203         rcoll = crda_get_file_ptr(db, dblen,
204                         sizeof(*rcoll) + num_rules * sizeof(uint32_t),
205                         country->reg_collection_ptr);
206
207         size_of_rd = sizeof(struct ieee80211_regdomain) +
208                 num_rules * sizeof(struct ieee80211_reg_rule);
209
210         rd = malloc(size_of_rd);
211         if (!rd)
212                 return NULL;
213
214         memset(rd, 0, size_of_rd);
215
216         rd->alpha2[0] = country->alpha2[0];
217         rd->alpha2[1] = country->alpha2[1];
218         rd->dfs_region = country->creqs & 0x3;
219         rd->n_reg_rules = num_rules;
220
221         for (i = 0; i < num_rules; i++) {
222                 reg_rule2rd(db, dblen, rcoll->reg_rule_ptrs[i],
223                         &rd->reg_rules[i]);
224         }
225
226         return rd;
227 }
228
229 struct ieee80211_regdomain *
230 reglib_get_rd_idx(unsigned int idx, const char *file)
231 {
232         int fd;
233         struct stat stat;
234         uint8_t *db;
235         struct regdb_file_header *header;
236         struct regdb_file_reg_country *countries;
237         int dblen, siglen, num_countries;
238         struct ieee80211_regdomain *rd = NULL;
239         struct regdb_file_reg_country *country;
240
241         fd = open(file, O_RDONLY);
242
243         if (fd < 0)
244                 return NULL;
245
246         if (fstat(fd, &stat))
247                 return NULL;
248
249         dblen = stat.st_size;
250
251         db = mmap(NULL, dblen, PROT_READ, MAP_PRIVATE, fd, 0);
252         if (db == MAP_FAILED)
253                 return NULL;
254
255         header = crda_get_file_ptr(db, dblen, sizeof(*header), 0);
256
257         if (ntohl(header->magic) != REGDB_MAGIC)
258                 return NULL;
259
260         if (ntohl(header->version) != REGDB_VERSION)
261                 return NULL;
262
263         siglen = ntohl(header->signature_length);
264         /* adjust dblen so later sanity checks don't run into the signature */
265         dblen -= siglen;
266
267         if (dblen <= (int)sizeof(*header))
268                 return NULL;
269
270         /* verify signature */
271         if (!crda_verify_db_signature(db, dblen, siglen))
272                 return NULL;
273
274         num_countries = ntohl(header->reg_country_num);
275         countries = crda_get_file_ptr(db, dblen,
276                         sizeof(struct regdb_file_reg_country) * num_countries,
277                         header->reg_country_ptr);
278
279         if (idx >= num_countries)
280                 return NULL;
281
282         country = countries + idx;
283
284         rd = country2rd(db, dblen, country);
285         if (!rd)
286                 return NULL;
287
288         return rd;
289 }
290
291 struct ieee80211_regdomain *
292 reglib_get_rd_alpha2(const char *alpha2, const char *file)
293 {
294         int fd;
295         struct stat stat;
296         uint8_t *db;
297         struct regdb_file_header *header;
298         struct regdb_file_reg_country *countries;
299         int dblen, siglen, num_countries;
300         struct ieee80211_regdomain *rd = NULL;
301         struct regdb_file_reg_country *country;
302         unsigned int i;
303         bool found_country = false;
304
305         fd = open(file, O_RDONLY);
306
307         if (fd < 0)
308                 return NULL;
309
310         if (fstat(fd, &stat))
311                 return NULL;
312
313         dblen = stat.st_size;
314
315         db = mmap(NULL, dblen, PROT_READ, MAP_PRIVATE, fd, 0);
316         if (db == MAP_FAILED)
317                 return NULL;
318
319         header = crda_get_file_ptr(db, dblen, sizeof(*header), 0);
320
321         if (ntohl(header->magic) != REGDB_MAGIC)
322                 return NULL;
323
324         if (ntohl(header->version) != REGDB_VERSION)
325                 return NULL;
326
327         siglen = ntohl(header->signature_length);
328         /* adjust dblen so later sanity checks don't run into the signature */
329         dblen -= siglen;
330
331         if (dblen <= (int)sizeof(*header))
332                 return NULL;
333
334         /* verify signature */
335         if (!crda_verify_db_signature(db, dblen, siglen))
336                 return NULL;
337
338         num_countries = ntohl(header->reg_country_num);
339         countries = crda_get_file_ptr(db, dblen,
340                         sizeof(struct regdb_file_reg_country) * num_countries,
341                         header->reg_country_ptr);
342
343         for (i = 0; i < num_countries; i++) {
344                 country = countries + i;
345                 if (memcmp(country->alpha2, alpha2, 2) == 0) {
346                         found_country = 1;
347                         break;
348                 }
349         }
350
351         if (!found_country)
352                 goto out;
353
354         rd = country2rd(db, dblen, country);
355         if (!rd)
356                 goto out;
357
358 out:
359         close(fd);
360         return rd;
361 }