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