blob: e6b9b3f195257606d3b85476ab3fa9ce2f190b3f [file] [log] [blame]
#include <errno.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "reglib.h"
#ifdef USE_OPENSSL
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif
#ifdef USE_GCRYPT
#include <gcrypt.h>
#endif
#include "reglib.h"
#ifdef USE_OPENSSL
#include "keys-ssl.c"
#endif
#ifdef USE_GCRYPT
#include "keys-gcrypt.c"
#endif
void *crda_get_file_ptr(__u8 *db, int dblen, int structlen, __be32 ptr)
{
__u32 p = ntohl(ptr);
if (p > dblen - structlen) {
fprintf(stderr, "Invalid database file, bad pointer!\n");
exit(3);
}
return (void *)(db + p);
}
/*
* Checks the validity of the signature found on the regulatory
* database against the array 'keys'. Returns 1 if there exists
* at least one key in the array such that the signature is valid
* against that key; 0 otherwise.
*/
int crda_verify_db_signature(__u8 *db, int dblen, int siglen)
{
#ifdef USE_OPENSSL
RSA *rsa;
__u8 hash[SHA_DIGEST_LENGTH];
unsigned int i;
int ok = 0;
rsa = RSA_new();
if (!rsa) {
fprintf(stderr, "Failed to create RSA key.\n");
goto out;
}
if (SHA1(db, dblen, hash) != hash) {
fprintf(stderr, "Failed to calculate SHA1 sum.\n");
RSA_free(rsa);
goto out;
}
for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
rsa->e = &keys[i].e;
rsa->n = &keys[i].n;
if (RSA_size(rsa) != siglen)
continue;
ok = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
db + dblen, siglen, rsa) == 1;
}
rsa->e = NULL;
rsa->n = NULL;
RSA_free(rsa);
#endif
#ifdef USE_GCRYPT
gcry_mpi_t mpi_e, mpi_n;
gcry_sexp_t rsa, signature, data;
__u8 hash[20];
unsigned int i;
int ok = 0;
/* initialise */
gcry_check_version(NULL);
/* hash the db */
gcry_md_hash_buffer(GCRY_MD_SHA1, hash, db, dblen);
if (gcry_sexp_build(&data, NULL, "(data (flags pkcs1) (hash sha1 %b))",
20, hash)) {
fprintf(stderr, "Failed to build data S-expression.\n");
goto out;
}
if (gcry_sexp_build(&signature, NULL, "(sig-val (rsa (s %b)))",
siglen, db + dblen)) {
fprintf(stderr, "Failed to build signature S-expression.\n");
goto out;
}
for (i = 0; (i < sizeof(keys)/sizeof(keys[0])) && (!ok); i++) {
if (gcry_mpi_scan(&mpi_e, GCRYMPI_FMT_USG,
keys[i].e, keys[i].len_e, NULL) ||
gcry_mpi_scan(&mpi_n, GCRYMPI_FMT_USG,
keys[i].n, keys[i].len_n, NULL)) {
fprintf(stderr, "Failed to convert numbers.\n");
goto out;
}
if (gcry_sexp_build(&rsa, NULL,
"(public-key (rsa (n %m) (e %m)))",
mpi_n, mpi_e)) {
fprintf(stderr, "Failed to build RSA S-expression.\n");
goto out;
}
ok = gcry_pk_verify(signature, data, rsa) == 0;
}
#endif
#if defined(USE_OPENSSL) || defined(USE_GCRYPT)
if (!ok)
fprintf(stderr, "Database signature verification failed.\n");
out:
return ok;
#else
return 1;
#endif
}
void reg_rule2rd(__u8 *db, int dblen,
__be32 ruleptr, struct ieee80211_reg_rule *rd_reg_rule)
{
struct regdb_file_reg_rule *rule;
struct regdb_file_freq_range *freq;
struct regdb_file_power_rule *power;
struct ieee80211_freq_range *rd_freq_range = &rd_reg_rule->freq_range;
struct ieee80211_power_rule *rd_power_rule = &rd_reg_rule->power_rule;
rule = crda_get_file_ptr(db, dblen, sizeof(*rule), ruleptr);
freq = crda_get_file_ptr(db, dblen, sizeof(*freq), rule->freq_range_ptr);
power = crda_get_file_ptr(db, dblen, sizeof(*power), rule->power_rule_ptr);
rd_freq_range->start_freq_khz = ntohl(freq->start_freq);
rd_freq_range->end_freq_khz = ntohl(freq->end_freq);
rd_freq_range->max_bandwidth_khz = ntohl(freq->max_bandwidth);
rd_power_rule->max_antenna_gain = ntohl(power->max_antenna_gain);
rd_power_rule->max_eirp = ntohl(power->max_eirp);
rd_reg_rule->flags = ntohl(rule->flags);
}
/* Converts a file regdomain to ieee80211_regdomain, easier to manage */
struct ieee80211_regdomain *country2rd(__u8 *db, int dblen,
struct regdb_file_reg_country *country)
{
struct regdb_file_reg_rules_collection *rcoll;
struct ieee80211_regdomain *rd;
int i, num_rules, size_of_rd;
rcoll = crda_get_file_ptr(db, dblen, sizeof(*rcoll),
country->reg_collection_ptr);
num_rules = ntohl(rcoll->reg_rule_num);
/* re-get pointer with sanity checking for num_rules */
rcoll = crda_get_file_ptr(db, dblen,
sizeof(*rcoll) + num_rules * sizeof(__be32),
country->reg_collection_ptr);
size_of_rd = sizeof(struct ieee80211_regdomain) +
num_rules * sizeof(struct ieee80211_reg_rule);
rd = malloc(size_of_rd);
if (!rd)
return NULL;
memset(rd, 0, size_of_rd);
rd->alpha2[0] = country->alpha2[0];
rd->alpha2[1] = country->alpha2[1];
rd->n_reg_rules = num_rules;
for (i = 0; i < num_rules; i++) {
reg_rule2rd(db, dblen, rcoll->reg_rule_ptrs[i],
&rd->reg_rules[i]);
}
return rd;
}
/* Sanity check on a regulatory rule */
int is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
{
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
__u32 freq_diff;
if (freq_range->start_freq_khz == 0 || freq_range->end_freq_khz == 0)
return 0;
if (freq_range->start_freq_khz > freq_range->end_freq_khz)
return 0;
freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
if (freq_diff == 0 || freq_range->max_bandwidth_khz > freq_diff)
return 0;
return 1;
}
void print_reg_rule(struct ieee80211_reg_rule *rule)
{
struct ieee80211_freq_range *freq;
struct ieee80211_power_rule *power;
freq = &rule->freq_range;
power = &rule->power_rule;
printf("\t(%.3f - %.3f @ %.3f), ",
((float)(freq->start_freq_khz))/1000.0,
((float)(freq->end_freq_khz))/1000.0,
((float)(freq->max_bandwidth_khz))/1000.0);
printf("(");
if (power->max_antenna_gain)
printf("%.2f, ", ((float)(power->max_antenna_gain)/100.0));
else
printf("N/A, ");
if (power->max_eirp)
printf("%.2f)", ((float)(power->max_eirp)/100.0));
else
printf("N/A)");
if (rule->flags & RRF_NO_OFDM)
printf(", NO-OFDM");
if (rule->flags & RRF_NO_CCK)
printf(", NO-CCK");
if (rule->flags & RRF_NO_INDOOR)
printf(", NO-INDOOR");
if (rule->flags & RRF_NO_OUTDOOR)
printf(", NO-OUTDOOR");
if (rule->flags & RRF_DFS)
printf(", DFS");
if (rule->flags & RRF_PTP_ONLY)
printf(", PTP-ONLY");
if (rule->flags & RRF_PTMP_ONLY)
printf(", PTMP-ONLY");
if (rule->flags & RRF_PASSIVE_SCAN)
printf(", PASSIVE-SCAN");
if (rule->flags & RRF_NO_IBSS)
printf(", NO-IBSS");
printf("\n");
}
void print_regdom(struct ieee80211_regdomain *rd)
{
unsigned int i;
printf("country %.2s:\n", rd->alpha2);
for (i = 0; i < rd->n_reg_rules; i++)
print_reg_rule(&rd->reg_rules[i]);
printf("\n");
}